Parcourir la source

[5357] Addressed comments after review:
- many more parameters are now inherited from shared-network to subnet4
- parameters now passed as const reference
- it is no longer possible to specify echo-client-id on shared-network
level (because it's a global parameter)
- example config commented properly

Tomek Mrugalski il y a 7 ans
Parent
commit
3e1e56f151

+ 56 - 12
doc/examples/kea4/shared-network.json

@@ -1,38 +1,84 @@
-
+// This is an example configuration file for DHCPv4 server in Kea.
+// It demonstrates an advanced feature called shared network. Typically, for
+// each physical link there is one IPv4 subnet that the server is expected
+// to manage. However, in some cases there is a need to configure more subnets
+// in the same physical location. The most common use case is an existing
+// subnet that grew past its original assumptions and ran out of addresses,
+// so the sysadmin needs to add another subnet on top of existing one.
 {
     "Dhcp4": {
+
+        // As with any other configuration, you need to tell Kea the interface
+        // names, so it would listen to incoming traffic.
         "interfaces-config": {
             "interfaces": [ "eth0" ]
         },
 
+        // You also need to tell where to store lease information.
+        // memfile is the backend that is easiest to set up.
         "lease-database": {
             "type": "memfile",
             "lfc-interval": 3600
         },
 
+        // Here the shared networks definition starts. shared-networks can
+        // contain a list of shared networks. There are many parameters
+        // that can be specfied here, so this example may be overwhelming
+        // at first, but the only mandatory parameter for each shared
+        // network is name. It must be unique. Typically, each shared
+        // subnet also needs to have at least two subnets to be functional,
+        // but if you really want to, you can define a degraded shared
+        // network that has 1 or even 0 subnets. This may come in handy
+        // when migrating between regular subnets and shared networks
+        // or when debugging a problem. It is not recommended to use
+        // 1 subnet per shared network, as there is extra processing
+        // overhead for shared networks.
         "shared-networks": [
         {
+            // Name of the shared network. It may be an arbitrary string
+            // and it must be unique among all shared networks.
+            "name": "frog",
+
+            // You may specify interface name if the shared network is
+            // reachable directly from the server.
             "interface": "eth1",
+
+            // You can specify many parameters that are allowed in subnet scope
+            // here. It's useful to put them here if they apply to all subnets
+            // in this shared network. It's likely that the most common
+            // parameter here will be option values defined with option-data.
             "match-client-id": false,
-            "name": "frog",
             "option-data": [ ],
             "rebind-timer": 150,
+
+            // If all the traffic coming from that shared subnet is reachable
+            // via relay and that relay always use the same IP address, you
+            // can specify that relay address here. Since this example shows
+            // a shared network reachable directly, we put 0.0.0.0 here.
+            // It would be better to skip the relay scope altogether, but
+            // it was left here for demonstration purposes.
             "relay": {
                 "ip-address": "0.0.0.0"
             },
+
+            // Timer values can be overridden here.
             "renew-timer": 100,
             "reservation-mode": "all",
+
+            // This starts a list of subnets allowed in this shared network.
+            // In our example, there are two subnets.
             "subnet4": [
                 {
-                    "4o6-interface": "",
-                    "4o6-interface-id": "",
-                    "4o6-subnet": "",
                     "id": 1,
                     "match-client-id": true,
                     "next-server": "0.0.0.0",
                     "option-data": [ ],
                     "pools": [ ],
                     "rebind-timer": 20,
+
+                    // You can override the value inherited from shared-network
+                    // here if your relay uses different IP addresses for
+                    // each subnet.
                     "relay": {
                         "ip-address": "0.0.0.0"
                     },
@@ -42,18 +88,12 @@
                     "valid-lifetime": 30
                 },
                 {
-                    "4o6-interface": "",
-                    "4o6-interface-id": "",
-                    "4o6-subnet": "",
                     "id": 2,
                     "match-client-id": true,
                     "next-server": "0.0.0.0",
                     "option-data": [ ],
                     "pools": [ ],
                     "rebind-timer": 20,
-                    "relay": {
-                        "ip-address": "0.0.0.0"
-                    },
                     "renew-timer": 10,
                     "reservation-mode": "all",
                     "subnet": "192.0.2.0/24",
@@ -63,6 +103,10 @@
             "valid-lifetime": 200
         } ], // end of shared-networks
 
+        // It is likely that in your network you'll have a mix or regular,
+        // "plain" subnets and shared networks. It is perfectly valid to mix
+        // them in the same config file.
+        //
         // This is regular subnet. It's not part of any shared-network.
         "subnet4": [
             {
@@ -72,6 +116,6 @@
                 "id": 3
             }
         ]
-        
+
     } // end of Dhcp4
 }

+ 0 - 1
src/bin/dhcp4/dhcp4_parser.yy

@@ -1002,7 +1002,6 @@ shared_network_param: name
                     | next_server
                     | relay
                     | reservation_mode
-                    | echo_client_id
                     | client_classes
                     | valid_lifetime
                     | unknown_map_entry

+ 3 - 3
src/bin/dhcp4/json_config_parser.cc

@@ -72,7 +72,7 @@ public:
     ///
     /// @throw DhcpConfigError if parameters are missing or
     /// or having incorrect values.
-    void parse(SrvConfigPtr cfg, ConstElementPtr global) {
+    void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
 
         // Set whether v4 server is supposed to echo back client-id
         // (yes = RFC6842 compatible, no = backward compatibility)
@@ -96,7 +96,7 @@ public:
     /// @throw BadValue if any pointer is missing
     /// @throw DhcpConfigError if there are duplicates (or other subnet defects)
     void
-    copySubnets4(const CfgSubnets4Ptr dest, const CfgSharedNetworks4Ptr from) {
+    copySubnets4(const CfgSubnets4Ptr& dest, const CfgSharedNetworks4Ptr& from) {
 
         if (!dest || !from) {
             isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null");
@@ -136,7 +136,7 @@ public:
     /// @param global global Dhcp4 scope
     /// @throw DhcpConfigError in case of issues found
     void
-    sanityChecks(SrvConfigPtr cfg, ConstElementPtr global) {
+    sanityChecks(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
 
         /// Shared network sanity checks
         const SharedNetwork4Collection* networks = cfg->getCfgSharedNetworks4()->getAll();

+ 64 - 11
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -611,17 +611,23 @@ public:
     /// @param t1 expected renew-timer value
     /// @param t2 expected rebind-timer value
     /// @param valid expected valid-lifetime value
-    void
+    /// @return the subnet that was examined
+    Subnet4Ptr
     checkSubnet(const Subnet4Collection& col, std::string subnet,
                 uint32_t t1, uint32_t t2, uint32_t valid) {
         const auto& index = col.get<SubnetPrefixIndexTag>();
         auto subnet_it = index.find(subnet);
-        ASSERT_NE(subnet_it, index.cend());
+        if (subnet_it == index.cend()) {
+            ADD_FAILURE() << "Unable to find expected subnet " << subnet;
+            return (Subnet4Ptr());
+        }
         Subnet4Ptr s = *subnet_it;
 
         EXPECT_EQ(t1, s->getT1());
         EXPECT_EQ(t2, s->getT2());
         EXPECT_EQ(valid, s->getValid());
+
+        return (s);
     }
 
     /// @brief This utility method attempts to configure using specified
@@ -5209,13 +5215,34 @@ TEST_F(Dhcp4ParserTest, sharedNetworks3subnets) {
 // - shared network to subnet
 // Also, it tests that more than one shared network can be defined.
 TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
+
+    // We need to fake the interfaces present, because we want to test
+    // interface names inheritance. However, there are sanity checks
+    // on subnet level that would refuse the value if the interface
+    // is not present.
+    IfaceMgrTestConfig iface_config(true);
+
+    // This config is structured in a way that the first shared
+    // subnet have many parameters defined. The first subnet
+    // should inherit them. The second subnet overrides all
+    // values and those values should be used, not those from
+    // shared network scope.
     string config = "{\n"
-        "\"renew-timer\": 1, \n"
+        "\"renew-timer\": 1, \n" // global values here
         "\"rebind-timer\": 2, \n"
         "\"valid-lifetime\": 4, \n"
         "\"shared-networks\": [ {\n"
-        "    \"name\": \"foo\"\n,"
+        "    \"name\": \"foo\"\n," // shared network values here
+        "    \"interface\": \"eth0\",\n"
+        "    \"match-client-id\": false,\n"
+        "    \"next-server\": \"1.2.3.4\",\n"
+        "    \"relay\": {\n"
+        "        \"ip-address\": \"5.6.7.8\"\n"
+        "    },\n"
+        "    \"reservation-mode\": \"out-of-pool\",\n"
         "    \"renew-timer\": 10,\n"
+        "    \"rebind-timer\": 20,\n"
+        "    \"valid-lifetime\": 40,\n"
         "    \"subnet4\": [\n"
         "    { \n"
         "        \"subnet\": \"192.0.1.0/24\",\n"
@@ -5224,10 +5251,17 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
         "    { \n"
         "        \"subnet\": \"192.0.2.0/24\",\n"
         "        \"pools\": [ { \"pool\": \"192.0.2.1-192.0.2.10\" } ],\n"
-        "        \"renew-timer\": 100\n"
+        "        \"renew-timer\": 100,\n"
+        "        \"rebind-timer\": 200,\n"
+        "        \"valid-lifetime\": 400,\n"
+        "        \"match-client-id\": true,\n"
+        "        \"next-server\": \"11.22.33.44\",\n"
+        "        \"relay\": {\n"
+        "            \"ip-address\": \"55.66.77.88\"\n"
+        "        },\n"
+        "        \"reservation-mode\": \"disabled\"\n"
         "    }\n"
         "    ]\n"
-
         " },\n"
         "{ // second shared-network starts here\n"
         "    \"name\": \"bar\",\n"
@@ -5265,13 +5299,28 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
     // derived from shared-network level. Other parameters a derived
     // from global scope to shared-network level and later again to
     // subnet4 level.
-    checkSubnet(*subs, "192.0.1.0/24", 10, 2, 4);
+    Subnet4Ptr s = checkSubnet(*subs, "192.0.1.0/24", 10, 20, 40);
+    ASSERT_TRUE(s);
+
+    // These are values derived from shared network scope:
+    EXPECT_EQ("eth0", s->getIface());
+    EXPECT_EQ(false, s->getMatchClientId());
+    EXPECT_EQ(IOAddress("1.2.3.4"), s->getSiaddr());
+    EXPECT_EQ(IOAddress("5.6.7.8"), s->getRelayInfo().addr_);
+    EXPECT_EQ(Network::HR_OUT_OF_POOL, s->getHostReservationMode());
 
     // 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
     // subnet4 level.
-    checkSubnet(*subs, "192.0.2.0/24", 100, 2, 4);
+    s = checkSubnet(*subs, "192.0.2.0/24", 100, 200, 400);
+
+    // These are values derived from shared network scope:
+    EXPECT_EQ("eth0", s->getIface());
+    EXPECT_EQ(true, s->getMatchClientId());
+    EXPECT_EQ(IOAddress("11.22.33.44"), s->getSiaddr());
+    EXPECT_EQ(IOAddress("55.66.77.88"), s->getRelayInfo().addr_);
+    EXPECT_EQ(Network::HR_DISABLED, s->getHostReservationMode());
 
     // Ok, now check the second shared subnet.
     net = nets->at(1);
@@ -5282,9 +5331,13 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
     EXPECT_EQ(1, subs->size());
 
     // This subnet should derive its renew-timer from global scope.
-    checkSubnet(*subs, "192.0.3.0/24", 1, 2, 4);
-
+    // All other parameters should have default values.
+    s = checkSubnet(*subs, "192.0.3.0/24", 1, 2, 4);
+    EXPECT_EQ("", s->getIface());
+    EXPECT_EQ(true, s->getMatchClientId());
+    EXPECT_EQ(IOAddress("0.0.0.0"), s->getSiaddr());
+    EXPECT_EQ(IOAddress("0.0.0.0"), s->getRelayInfo().addr_);
+    EXPECT_EQ(Network::HR_ALL, s->getHostReservationMode());
 }
 
-
 }

+ 1 - 0
src/bin/dhcp6/dhcp6_parser.yy

@@ -982,6 +982,7 @@ shared_network_params: shared_network_param
 shared_network_param: name
                     | subnet6_list
                     | interface
+                    | interface_id
                     | renew_timer
                     | rebind_timer
                     | option_data_list

+ 147 - 7
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -322,18 +322,24 @@ public:
     /// @param t2 expected rebind-timer value
     /// @param preferred expected preferred-lifetime value
     /// @param valid expected valid-lifetime value
-    void
+    /// @return the subnet that was examined
+    Subnet6Ptr
     checkSubnet(const Subnet6Collection& col, std::string subnet,
                 uint32_t t1, uint32_t t2, uint32_t pref, uint32_t valid) {
         const auto& index = col.get<SubnetPrefixIndexTag>();
         auto subnet_it = index.find(subnet);
-        ASSERT_NE(subnet_it, index.cend());
+        if (subnet_it == index.cend()) {
+            ADD_FAILURE() << "Unable to find expected subnet " << subnet;
+            return (Subnet6Ptr());
+        }
         Subnet6Ptr s = *subnet_it;
 
         EXPECT_EQ(t1, s->getT1());
         EXPECT_EQ(t2, s->getT2());
         EXPECT_EQ(pref, s->getPreferred());
         EXPECT_EQ(valid, s->getValid());
+
+        return (s);
     }
 
     /// @brief Returns an interface configuration used by the most of the
@@ -863,7 +869,7 @@ TEST_F(Dhcp6ParserTest, emptySubnet) {
 
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
-    
+
 
     // returned value should be 0 (success)
     checkResult(status, 0);
@@ -5558,6 +5564,21 @@ TEST_F(Dhcp6ParserTest, sharedNetworks3subnets) {
 // - shared network to subnet
 // Also, it tests that more than one shared network can be defined.
 TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
+
+    // We need to fake the interfaces present, because we want to test
+    // interface names inheritance. However, there are sanity checks
+    // on subnet level that would refuse the value if the interface
+    // is not present.
+    IfaceMgrTestConfig iface_config(true);
+
+    // Build some expected interface-id values.
+    const string text1 = "oneone";
+    const string text2 = "twotwo";
+    OptionBuffer buffer1 = OptionBuffer(text1.begin(), text1.end());
+    OptionBuffer buffer2 = OptionBuffer(text2.begin(), text2.end());
+    Option iface_id1(Option::V6, D6O_INTERFACE_ID, buffer1);
+    Option iface_id2(Option::V6, D6O_INTERFACE_ID, buffer2);
+
     string config = "{\n"
         "\"renew-timer\": 1, \n"
         "\"rebind-timer\": 2, \n"
@@ -5566,6 +5587,121 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
         "\"shared-networks\": [ {\n"
         "    \"name\": \"foo\"\n,"
         "    \"renew-timer\": 10,\n"
+        "    \"rebind-timer\": 20, \n"
+        "    \"preferred-lifetime\": 30,\n"
+        "    \"valid-lifetime\": 40, \n"
+        "    \"interface-id\": \"oneone\",\n"
+        "    \"relay\": {\n"
+        "        \"ip-address\": \"1111::1\"\n"
+        "    },\n"
+        "    \"rapid-commit\": true,\n"
+        "    \"reservation-mode\": \"disabled\",\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,"
+        "        \"rebind-timer\": 200, \n"
+        "        \"preferred-lifetime\": 300,\n"
+        "        \"relay\": {\n"
+        "            \"ip-address\": \"2222::2\"\n"
+        "        },\n"
+        "        \"valid-lifetime\": 400, \n"
+        "        \"interface-id\": \"twotwo\",\n"
+        "        \"rapid-commit\": false,\n"
+        "        \"reservation-mode\": \"out-of-pool\"\n"
+        "    }\n"
+        "    ]\n"
+        " },\n"
+        "{ // second shared-network starts here\n"
+        "    \"name\": \"bar\",\n"
+        "    \"subnet6\": [\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();
+
+    // Two shared networks are expeced.
+    ASSERT_TRUE(cfg_net);
+    const SharedNetwork6Collection* nets = cfg_net->getAll();
+    ASSERT_TRUE(nets);
+    ASSERT_EQ(2, nets->size());
+
+    // Let's check the first one.
+    SharedNetwork6Ptr net = nets->at(0);
+    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.
+    Subnet6Ptr s = checkSubnet(*subs, "2001:db1::/48", 10, 20, 30, 40);
+    ASSERT_TRUE(s);
+    ASSERT_TRUE(s->getInterfaceId());
+    EXPECT_TRUE(iface_id1.equals(s->getInterfaceId()));
+    EXPECT_EQ(IOAddress("1111::1"), s->getRelayInfo().addr_);
+    EXPECT_EQ(true, s->getRapidCommit());
+    EXPECT_EQ(Network::HR_DISABLED, s->getHostReservationMode());
+
+    // 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.
+    s = checkSubnet(*subs, "2001:db2::/48", 100, 200, 300, 400);
+    ASSERT_TRUE(s->getInterfaceId());
+    EXPECT_TRUE(iface_id2.equals(s->getInterfaceId()));
+    EXPECT_EQ(IOAddress("2222::2"), s->getRelayInfo().addr_);
+    EXPECT_EQ(false, s->getRapidCommit());
+    EXPECT_EQ(Network::HR_OUT_OF_POOL, s->getHostReservationMode());
+
+    // Ok, now check the second shared subnet.
+    net = nets->at(1);
+    ASSERT_TRUE(net);
+
+    subs = net->getAllSubnets();
+    ASSERT_TRUE(subs);
+    EXPECT_EQ(1, subs->size());
+
+    // This subnet should derive its renew-timer from global scope.
+    s = checkSubnet(*subs, "2001:db3::/48", 1, 2, 3, 4);
+    EXPECT_FALSE(s->getInterfaceId());
+    EXPECT_EQ(IOAddress("::"), s->getRelayInfo().addr_);
+    EXPECT_EQ(false, s->getRapidCommit());
+    EXPECT_EQ(Network::HR_ALL, s->getHostReservationMode());
+}
+
+// Since it is not allowed to define both interface-id and interface
+// for the same subnet, we need dedicated test that will check
+// interface separately.
+TEST_F(Dhcp6ParserTest, sharedNetworksDeriveInterfaces) {
+
+    // We need to fake the interfaces present, because we want to test
+    // interface names inheritance. However, there are sanity checks
+    // on subnet level that would refuse the value if the interface
+    // is not present.
+    IfaceMgrTestConfig iface_config(true);
+
+    string config = "{\n"
+        "\"shared-networks\": [ {\n"
+        "    \"name\": \"foo\"\n,"
+        "    \"interface\": \"eth0\",\n"
         "    \"subnet6\": [\n"
         "    { \n"
         "        \"subnet\": \"2001:db1::/48\",\n"
@@ -5574,7 +5710,7 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
         "    { \n"
         "        \"subnet\": \"2001:db2::/48\",\n"
         "        \"pools\": [ { \"pool\": \"2001:db2::/64\" } ],\n"
-        "        \"renew-timer\": 100\n"
+        "        \"interface\": \"eth0\"\n"
         "    }\n"
         "    ]\n"
         " },\n"
@@ -5613,13 +5749,16 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
     // 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);
+    Subnet6Ptr s = checkSubnet(*subs, "2001:db1::/48", 900, 1800, 3600, 7200);
+    ASSERT_TRUE(s);
+    EXPECT_EQ("eth0", s->getIface());
 
     // 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);
+    checkSubnet(*subs, "2001:db2::/48", 900, 1800, 3600, 7200);
+    EXPECT_EQ("eth0", s->getIface());
 
     // Ok, now check the second shared subnet.
     net = nets->at(1);
@@ -5630,7 +5769,8 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
     EXPECT_EQ(1, subs->size());
 
     // This subnet should derive its renew-timer from global scope.
-    checkSubnet(*subs, "2001:db3::/48", 1, 2, 3, 4);
+    s = checkSubnet(*subs, "2001:db3::/48", 900, 1800, 3600, 7200);
+    EXPECT_EQ("", s->getIface());
 }
 
 };

+ 48 - 10
src/lib/dhcpsrv/parsers/simple_parser4.cc

@@ -7,6 +7,7 @@
 #include <dhcpsrv/parsers/simple_parser4.h>
 #include <cc/data.h>
 #include <boost/foreach.hpp>
+#include <iostream>
 
 using namespace isc::data;
 
@@ -66,6 +67,12 @@ const SimpleDefaults SimpleParser4::GLOBAL4_DEFAULTS = {
 };
 
 /// @brief This table defines default values for each IPv4 subnet.
+///
+/// Note: When updating this array, please also update SHARED_SUBNET4_DEFAULTS
+/// below. In most cases, those two should be kept in sync, except cases
+/// where a parameter can be derived from shared-networks, but is not
+/// defined on global level. Currently there are two such parameters:
+/// interface and reservation-mode
 const SimpleDefaults SimpleParser4::SUBNET4_DEFAULTS = {
     { "id",               Element::integer, "0" }, // 0 means autogenerate
     { "interface",        Element::string,  "" },
@@ -76,24 +83,51 @@ const SimpleDefaults SimpleParser4::SUBNET4_DEFAULTS = {
     { "4o6-subnet",       Element::string,  "" },
 };
 
+/// @brief This table defines default values for each IPv4 subnet that is
+///        part of a shared network
+///
+/// This is mostly the same as @ref SUBNET4_DEFAULTS, except two parameters
+/// that can be derived from shared-network, but cannot from global scope.
+/// Those are: interface and reservation-mode.
+const SimpleDefaults SimpleParser4::SHARED_SUBNET4_DEFAULTS = {
+    { "id",               Element::integer, "0" }, // 0 means autogenerate
+    { "client-class",     Element::string,  "" },
+    { "4o6-interface",    Element::string,  "" },
+    { "4o6-interface-id", Element::string,  "" },
+    { "4o6-subnet",       Element::string,  "" },
+};
+
+/// @brief This table defines default values for each IPv4 shared network.
+const SimpleDefaults SimpleParser4::SHARED_NETWORK4_DEFAULTS = {
+    { "interface",        Element::string, "" },
+    { "reservation-mode", Element::string, "all" }
+};
+
 /// @brief This table defines default values for interfaces for DHCPv4.
 const SimpleDefaults SimpleParser4::IFACE4_DEFAULTS = {
     { "re-detect", Element::boolean, "true" }
 };
 
-/// @brief List of parameters that can be inherited from the global to subnet4 scope.
+/// @brief List of parameters that can be inherited to subnet4 scope.
 ///
 /// Some parameters may be defined on both global (directly in Dhcp4) and
 /// subnet (Dhcp4/subnet4/...) scope. If not defined in the subnet scope,
 /// the value is being inherited (derived) from the global scope. This
 /// array lists all of such parameters.
-const ParamsList SimpleParser4::INHERIT_GLOBAL_TO_SUBNET4 = {
-    "renew-timer",
-    "rebind-timer",
-    "valid-lifetime",
+///
+/// This list is also used for inheriting from global to shared networks
+/// and from shared networks to subnets within it.
+const ParamsList SimpleParser4::INHERIT_TO_SUBNET4 = {
+    "interface",
     "match-client-id",
-    "next-server"
+    "next-server",
+    "rebind-timer",
+    "relay",
+    "renew-timer",
+    "reservation-mode",
+    "valid-lifetime"
 };
+
 /// @}
 
 /// ---------------------------------------------------------------------------
@@ -137,9 +171,12 @@ size_t SimpleParser4::setAllDefaults(isc::data::ElementPtr global) {
     ConstElementPtr shared = global->get("shared-networks");
     if (shared) {
         BOOST_FOREACH(ElementPtr net, shared->listValue()) {
+
+            cnt += setDefaults(net, SHARED_NETWORK4_DEFAULTS);
+
             ConstElementPtr subs = net->get("subnet4");
             if (subs) {
-                cnt += setListDefaults(subs, SUBNET4_DEFAULTS);
+                cnt += setListDefaults(subs, SHARED_SUBNET4_DEFAULTS);
             }
         }
     }
@@ -155,7 +192,7 @@ size_t SimpleParser4::deriveParameters(isc::data::ElementPtr global) {
     if (subnets) {
         BOOST_FOREACH(ElementPtr single_subnet, subnets->listValue()) {
             cnt += SimpleParser::deriveParams(global, single_subnet,
-                                              INHERIT_GLOBAL_TO_SUBNET4);
+                                              INHERIT_TO_SUBNET4);
         }
     }
 
@@ -169,16 +206,17 @@ size_t SimpleParser4::deriveParameters(isc::data::ElementPtr global) {
             // if defined there.
             // Then try to inherit them from global.
             cnt += SimpleParser::deriveParams(global, net,
-                                              INHERIT_GLOBAL_TO_SUBNET4);
+                                              INHERIT_TO_SUBNET4);
 
             // Now we need to go thrugh all the subnets in this net.
             subnets = net->get("subnet4");
             if (subnets) {
                 BOOST_FOREACH(ElementPtr single_subnet, subnets->listValue()) {
                     cnt += SimpleParser::deriveParams(net, single_subnet,
-                                                      INHERIT_GLOBAL_TO_SUBNET4);
+                                                      INHERIT_TO_SUBNET4);
                 }
             }
+
         }
     }
 

+ 3 - 1
src/lib/dhcpsrv/parsers/simple_parser4.h

@@ -41,8 +41,10 @@ public:
     static const isc::data::SimpleDefaults OPTION4_DEFAULTS;
     static const isc::data::SimpleDefaults GLOBAL4_DEFAULTS;
     static const isc::data::SimpleDefaults SUBNET4_DEFAULTS;
+    static const isc::data::SimpleDefaults SHARED_SUBNET4_DEFAULTS;
+    static const isc::data::SimpleDefaults SHARED_NETWORK4_DEFAULTS;
     static const isc::data::SimpleDefaults IFACE4_DEFAULTS;
-    static const isc::data::ParamsList INHERIT_GLOBAL_TO_SUBNET4;
+    static const isc::data::ParamsList INHERIT_TO_SUBNET4;
 };
 
 };

+ 33 - 7
src/lib/dhcpsrv/parsers/simple_parser6.cc

@@ -73,6 +73,21 @@ const SimpleDefaults SimpleParser6::SUBNET6_DEFAULTS = {
     { "interface-id",     Element::string,  "" },
 };
 
+/// @brief This table defines default values for each IPv6 subnet.
+const SimpleDefaults SimpleParser6::SHARED_SUBNET6_DEFAULTS = {
+    { "id",               Element::integer, "0" }, // 0 means autogenerate
+    { "client-class",     Element::string,  "" }
+};
+
+/// @brief This table defines default values for each IPv6 shared network.
+const SimpleDefaults SimpleParser6::SHARED_NETWORK6_DEFAULTS = {
+    { "interface",        Element::string, "" },
+    { "interface-id",     Element::string,  "" },
+    { "reservation-mode", Element::string, "all" },
+    { "rapid-commit",     Element::boolean, "false" } // rapid-commit disabled by default
+};
+
+
 /// @brief This table defines default values for interfaces for DHCPv6.
 const SimpleDefaults SimpleParser6::IFACE6_DEFAULTS = {
     { "re-detect", Element::boolean, "true" }
@@ -84,10 +99,18 @@ const SimpleDefaults SimpleParser6::IFACE6_DEFAULTS = {
 /// subnet (Dhcp6/subnet6/...) scope. If not defined in the subnet scope,
 /// the value is being inherited (derived) from the global scope. This
 /// array lists all of such parameters.
-const ParamsList SimpleParser6::INHERIT_GLOBAL_TO_SUBNET6 = {
-    "renew-timer",
-    "rebind-timer",
+///
+/// This list is also used for inheriting from global to shared networks
+/// and from shared networks to subnets within it.
+const ParamsList SimpleParser6::INHERIT_TO_SUBNET6 = {
+    "interface",
+    "interface-id",
     "preferred-lifetime",
+    "rapid-commit",
+    "rebind-timer",
+    "relay",
+    "renew-timer",
+    "reservation-mode",
     "valid-lifetime"
 };
 /// @}
@@ -135,9 +158,12 @@ size_t SimpleParser6::setAllDefaults(isc::data::ElementPtr global) {
     ConstElementPtr shared = global->get("shared-networks");
     if (shared) {
         BOOST_FOREACH(ElementPtr net, shared->listValue()) {
+
+            cnt += setDefaults(net, SHARED_NETWORK6_DEFAULTS);
+
             ConstElementPtr subs = net->get("subnet6");
             if (subs) {
-                cnt += setListDefaults(subs, SUBNET6_DEFAULTS);
+                cnt += setListDefaults(subs, SHARED_SUBNET6_DEFAULTS);
             }
         }
     }
@@ -152,7 +178,7 @@ size_t SimpleParser6::deriveParameters(isc::data::ElementPtr global) {
     if (subnets) {
         BOOST_FOREACH(ElementPtr single_subnet, subnets->listValue()) {
             cnt += SimpleParser::deriveParams(global, single_subnet,
-                                              INHERIT_GLOBAL_TO_SUBNET6);
+                                              INHERIT_TO_SUBNET6);
         }
     }
 
@@ -166,14 +192,14 @@ size_t SimpleParser6::deriveParameters(isc::data::ElementPtr global) {
             // if defined there.
             // Then try to inherit them from global.
             cnt += SimpleParser::deriveParams(global, net,
-                                              INHERIT_GLOBAL_TO_SUBNET6);
+                                              INHERIT_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);
+                                                      INHERIT_TO_SUBNET6);
                 }
             }
         }

+ 3 - 1
src/lib/dhcpsrv/parsers/simple_parser6.h

@@ -42,8 +42,10 @@ public:
     static const isc::data::SimpleDefaults OPTION6_DEFAULTS;
     static const isc::data::SimpleDefaults GLOBAL6_DEFAULTS;
     static const isc::data::SimpleDefaults SUBNET6_DEFAULTS;
+    static const isc::data::SimpleDefaults SHARED_SUBNET6_DEFAULTS;
+    static const isc::data::SimpleDefaults SHARED_NETWORK6_DEFAULTS;
     static const isc::data::SimpleDefaults IFACE6_DEFAULTS;
-    static const isc::data::ParamsList INHERIT_GLOBAL_TO_SUBNET6;
+    static const isc::data::ParamsList INHERIT_TO_SUBNET6;
 };
 
 };