]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#488] DHCP subnet and network parsers allow unspecified parameters.
authorMarcin Siodelski <marcin@isc.org>
Mon, 4 Mar 2019 11:02:39 +0000 (12:02 +0100)
committerMarcin Siodelski <marcin@isc.org>
Thu, 7 Mar 2019 13:00:35 +0000 (08:00 -0500)
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

index ad0cfe43dac89b71aa42ea9b07701c447f52bd20..c698534400b395fb94375e1f1ab2e520a80c7c3a 100644 (file)
@@ -683,15 +683,17 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
         t2 = getInteger(params, "rebind-timer");
     }
 
-    // The valid-lifetime is mandatory. It may be specified for a
-    // particular subnet. If not, the global value should be present.
-    // If there is no global value, exception is thrown.
-    Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
+    Triplet<uint32_t> valid;
+    if (params->contains("valid-lifetime")) {
+        valid = getInteger(params, "valid-lifetime");
+    }
 
     // Subnet ID is optional. If it is not supplied the value of 0 is used,
-    // which means autogenerate. The value was inserted earlier by calling
-    // SimpleParser4::setAllDefaults.
-    SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
+    // which means autogenerate.
+    SubnetID subnet_id = 0;
+    if (params->contains("id")) {
+        subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
+    }
 
     stringstream s;
     s << addr << "/" << static_cast<int>(len) << " with params: ";
@@ -709,95 +711,106 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
     Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
     subnet_ = subnet4;
 
-    // Set the match-client-id value for the subnet. It is always present.
-    // If not explicitly specified, the default value was filled in when
-    // SimpleParser4::setAllDefaults was called.
-    bool match_client_id = getBoolean(params, "match-client-id");
-    subnet4->setMatchClientId(match_client_id);
+    // Set the match-client-id value for the subnet.
+    if (params->contains("match-client-id")) {
+        bool match_client_id = getBoolean(params, "match-client-id");
+        subnet4->setMatchClientId(match_client_id);
+    }
 
-    // Set the authoritative value for the subnet. It is always present.
-    // If not explicitly specified, the default value was filled in when
-    // SimpleParser4::setAllDefaults was called.
-    bool authoritative = getBoolean(params, "authoritative");
-    subnet4->setAuthoritative(authoritative);
+    // Set the authoritative value for the subnet.
+    if (params->contains("authoritative")) {
+        bool authoritative = getBoolean(params, "authoritative");
+        subnet4->setAuthoritative(authoritative);
+    }
 
     // Set next-server. The default value is 0.0.0.0. Nevertheless, the
     // user could have messed that up by specifying incorrect value.
     // To avoid using 0.0.0.0, user can specify "".
-    string next_server;
-    try {
-        next_server = getString(params, "next-server");
-        if (!next_server.empty()) {
-            subnet4->setSiaddr(IOAddress(next_server));
-        }
-    } catch (...) {
-        ConstElementPtr next = params->get("next-server");
-        string pos;
-        if (next) {
-            pos = next->getPosition().str();
-        } else {
-            pos = params->getPosition().str();
+    if (params->contains("next-server")) {
+        string next_server;
+        try {
+            next_server = getString(params, "next-server");
+            if (!next_server.empty()) {
+                subnet4->setSiaddr(IOAddress(next_server));
+            }
+        } catch (...) {
+            ConstElementPtr next = params->get("next-server");
+            string pos;
+            if (next) {
+                pos = next->getPosition().str();
+            } else {
+                pos = params->getPosition().str();
+            }
+            isc_throw(DhcpConfigError, "invalid parameter next-server : "
+                      << next_server << "(" << pos << ")");
         }
-        isc_throw(DhcpConfigError, "invalid parameter next-server : "
-                  << next_server << "(" << pos << ")");
     }
 
     // Set server-hostname.
-    std::string sname = getString(params, "server-hostname");
-    if (!sname.empty()) {
-        if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
-            ConstElementPtr error = params->get("server-hostname");
-            isc_throw(DhcpConfigError, "server-hostname must be at most "
-                      << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
-                      << sname.length() << " ("
-                      << error->getPosition() << ")");
+    if (params->contains("server-hostname")) {
+        std::string sname = getString(params, "server-hostname");
+        if (!sname.empty()) {
+            if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
+                ConstElementPtr error = params->get("server-hostname");
+                isc_throw(DhcpConfigError, "server-hostname must be at most "
+                          << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
+                          << sname.length() << " ("
+                          << error->getPosition() << ")");
+            }
+            subnet4->setSname(sname);
         }
-        subnet4->setSname(sname);
     }
 
     // Set boot-file-name.
-    std::string filename =getString(params, "boot-file-name");
-    if (!filename.empty()) {
-        if (filename.length() > Pkt4::MAX_FILE_LEN) {
-            ConstElementPtr error = params->get("boot-file-name");
-            isc_throw(DhcpConfigError, "boot-file-name must be at most "
-                      << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
-                      << filename.length() << " ("
-                      << error->getPosition() << ")");
+    if (params->contains("boot-file-name")) {
+        std::string filename =getString(params, "boot-file-name");
+        if (!filename.empty()) {
+            if (filename.length() > Pkt4::MAX_FILE_LEN) {
+                ConstElementPtr error = params->get("boot-file-name");
+                isc_throw(DhcpConfigError, "boot-file-name must be at most "
+                          << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
+                          << filename.length() << " ("
+                          << error->getPosition() << ")");
+            }
+            subnet4->setFilename(filename);
         }
-        subnet4->setFilename(filename);
     }
 
     // Get interface name. If it is defined, then the subnet is available
     // directly over specified network interface.
-    std::string iface = getString(params, "interface");
-    if (!iface.empty()) {
-        if (!IfaceMgr::instance().getIface(iface)) {
-            ConstElementPtr error = params->get("interface");
-            isc_throw(DhcpConfigError, "Specified network interface name " << iface
-                      << " for subnet " << subnet4->toText()
-                      << " is not present in the system ("
-                      << error->getPosition() << ")");
-        }
+    if (params->contains("interface")) {
+        std::string iface = getString(params, "interface");
+        if (!iface.empty()) {
+            if (!IfaceMgr::instance().getIface(iface)) {
+                ConstElementPtr error = params->get("interface");
+                isc_throw(DhcpConfigError, "Specified network interface name " << iface
+                          << " for subnet " << subnet4->toText()
+                          << " is not present in the system ("
+                          << error->getPosition() << ")");
+            }
 
-        subnet4->setIface(iface);
+            subnet4->setIface(iface);
+        }
     }
 
-    // Let's set host reservation mode. If not specified, the default value of
-    // all will be used.
-    try {
-        std::string hr_mode = getString(params, "reservation-mode");
-        subnet4->setHostReservationMode(hrModeFromText(hr_mode));
-    } catch (const BadValue& ex) {
-       isc_throw(DhcpConfigError, "Failed to process specified value "
-                  " of reservation-mode parameter: " << ex.what()
-                  << "(" << getPosition("reservation-mode", params) << ")");
+    // Let's set host reservation mode.
+    if (params->contains("reservation-mode")) {
+        try {
+            std::string hr_mode = getString(params, "reservation-mode");
+            subnet4->setHostReservationMode(hrModeFromText(hr_mode));
+        } catch (const BadValue& ex) {
+            isc_throw(DhcpConfigError, "Failed to process specified value "
+                      " of reservation-mode parameter: " << ex.what()
+                      << "(" << getPosition("reservation-mode", params) << ")");
+        }
     }
 
     // Try setting up client class.
-    string client_class = getString(params, "client-class");
-    if (!client_class.empty()) {
-        subnet4->allowClientClass(client_class);
+    if (params->contains("client-class")) {
+        string client_class = getString(params, "client-class");
+        if (!client_class.empty()) {
+            subnet4->allowClientClass(client_class);
+        }
     }
 
     // Try setting up required client classes.
@@ -815,44 +828,48 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
         }
     }
 
-    // 4o6 specific parameter: 4o6-interface. If not explicitly specified,
-    // it will have the default value of "".
-    string iface4o6 = getString(params, "4o6-interface");
-    if (!iface4o6.empty()) {
-        subnet4->get4o6().setIface4o6(iface4o6);
-        subnet4->get4o6().enabled(true);
+    // 4o6 specific parameter: 4o6-interface.
+    if (params->contains("4o6-interface")) {
+        string iface4o6 = getString(params, "4o6-interface");
+        if (!iface4o6.empty()) {
+            subnet4->get4o6().setIface4o6(iface4o6);
+            subnet4->get4o6().enabled(true);
+        }
     }
 
-    // 4o6 specific parameter: 4o6-subnet. If not explicitly specified, it
-    // will have the default value of "".
-    string subnet4o6 = getString(params, "4o6-subnet");
-    if (!subnet4o6.empty()) {
-        size_t slash = subnet4o6.find("/");
-        if (slash == std::string::npos) {
-            isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
-                      << subnet4o6 << ", expected format: prefix6/length");
-        }
-        string prefix = subnet4o6.substr(0, slash);
-        string lenstr = subnet4o6.substr(slash + 1);
+    // 4o6 specific parameter: 4o6-subnet.
+    if (params->contains("4o6-subnet")) {
+        string subnet4o6 = getString(params, "4o6-subnet");
+        if (!subnet4o6.empty()) {
+            size_t slash = subnet4o6.find("/");
+            if (slash == std::string::npos) {
+                isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
+                          << subnet4o6 << ", expected format: prefix6/length");
+            }
+            string prefix = subnet4o6.substr(0, slash);
+            string lenstr = subnet4o6.substr(slash + 1);
 
-        uint8_t len = 128;
-        try {
-            len = boost::lexical_cast<unsigned int>(lenstr.c_str());
-        } catch (const boost::bad_lexical_cast &) {
-            isc_throw(DhcpConfigError, "Invalid prefix length specified in "
-                      "4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
+            uint8_t len = 128;
+            try {
+                len = boost::lexical_cast<unsigned int>(lenstr.c_str());
+            } catch (const boost::bad_lexical_cast &) {
+                isc_throw(DhcpConfigError, "Invalid prefix length specified in "
+                          "4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
+            }
+            subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
+            subnet4->get4o6().enabled(true);
         }
-        subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
-        subnet4->get4o6().enabled(true);
     }
 
     // Try 4o6 specific parameter: 4o6-interface-id
-    std::string ifaceid = getString(params, "4o6-interface-id");
-    if (!ifaceid.empty()) {
-        OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
-        OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
-        subnet4->get4o6().setInterfaceId(opt);
-        subnet4->get4o6().enabled(true);
+    if (params->contains("4o6-interface-id")) {
+        std::string ifaceid = getString(params, "4o6-interface-id");
+        if (!ifaceid.empty()) {
+            OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
+            OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+            subnet4->get4o6().setInterfaceId(opt);
+            subnet4->get4o6().enabled(true);
+        }
     }
 
     /// client-class processing is now generic and handled in the common
@@ -865,24 +882,38 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
     // Copy options to the subnet configuration.
     options_->copyTo(*subnet4->getCfgOption());
 
-    bool calculate_tee_times = getBoolean(params, "calculate-tee-times");
-    subnet4->setCalculateTeeTimes(calculate_tee_times);
-    float t2_percent = getDouble(params, "t2-percent");
-    float t1_percent = getDouble(params, "t1-percent");
+    bool calculate_tee_times = subnet4->getCalculateTeeTimes();
+    if (params->contains("calculate-tee-times")) {
+        bool calculate_tee_times = getBoolean(params, "calculate-tee-times");
+        subnet4->setCalculateTeeTimes(calculate_tee_times);
+    }
+
+    Optional<double> t2_percent;
+    if (params->contains("t2-percent")) {
+        t2_percent = getDouble(params, "t2-percent");
+    }
+
+    Optional<double> t1_percent;
+    if (params->contains("t1-percent")) {
+        t1_percent = getDouble(params, "t1-percent");
+    }
     if (calculate_tee_times) {
-        if (t2_percent <= 0.0 || t2_percent >= 1.0) {
-            isc_throw(DhcpConfigError, "t2-percent:  " << t2_percent
+        if (!t2_percent.unspecified() && ((t2_percent.get() <= 0.0) ||
+                                          (t2_percent.get() >= 1.0))) {
+            isc_throw(DhcpConfigError, "t2-percent:  " << t2_percent.get()
                       << " is invalid, it must be greater than 0.0 and less than 1.0");
         }
 
-        if (t1_percent <= 0.0 || t1_percent >= 1.0) {
-            isc_throw(DhcpConfigError, "t1-percent:  " << t1_percent
+        if (!t1_percent.unspecified() && ((t1_percent.get() <= 0.0) ||
+                                          (t1_percent.get() >= 1.0))) {
+            isc_throw(DhcpConfigError, "t1-percent:  " << t1_percent.get()
                       << " is invalid it must be greater than 0.0 and less than 1.0");
         }
 
-        if (t1_percent >= t2_percent) {
-            isc_throw(DhcpConfigError, "t1-percent:  " << t1_percent
-                      << " is invalid, it must be less than t2-percent: " << t2_percent);
+        if (!t1_percent.unspecified() && !t2_percent.unspecified() &&
+            (t1_percent.get() >= t2_percent.get())) {
+            isc_throw(DhcpConfigError, "t1-percent:  " << t1_percent.get()
+                      << " is invalid, it must be less than t2-percent: " << t2_percent.get());
         }
 
     }
@@ -1131,15 +1162,26 @@ Subnet6ConfigParser::duplicate_option_warning(uint32_t code,
 void
 Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
                                 asiolink::IOAddress addr, uint8_t len) {
-    // Get all 'time' parameters using inheritance.
-    // If the subnet-specific value is defined then use it, else
-    // use the global value. The global value must always be
-    // present. If it is not, it is an internal error and exception
-    // is thrown.
-    Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
-    Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
-    Triplet<uint32_t> pref = getInteger(params, "preferred-lifetime");
-    Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
+    // Get all 'time' parameters.
+    Triplet<uint32_t> t1;
+    if (params->contains("renew-timer")) {
+        t1 = getInteger(params, "renew-timer");
+    }
+
+    Triplet<uint32_t> t2;
+    if (params->contains("rebind-timer")) {
+        t2 = getInteger(params, "rebind-timer");
+    }
+
+    Triplet<uint32_t> pref;
+    if (params->contains("preferred-lifetime")) {
+        pref = getInteger(params, "preferred-lifetime");
+    }
+
+    Triplet<uint32_t> valid;
+    if (params->contains("valid-lifetime")) {
+        valid = getInteger(params, "valid-lifetime");
+    }
 
     // Subnet ID is optional. If it is not supplied the value of 0 is used,
     // which means autogenerate. The value was inserted earlier by calling
@@ -1148,7 +1190,10 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
 
     // We want to log whether rapid-commit is enabled, so we get this
     // before the actual subnet creation.
-    bool rapid_commit = getBoolean(params, "rapid-commit");
+    Optional<bool> rapid_commit;
+    if (params->contains("rapid-commit")) {
+        rapid_commit = getBoolean(params, "rapid-commit");
+    }
 
     std::ostringstream output;
     output << addr << "/" << static_cast<int>(len)
@@ -1162,21 +1207,31 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
 
     // Create a new subnet.
     Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
-                                       subnet_id);
+                                   subnet_id);
     subnet_.reset(subnet6);
 
     // Enable or disable Rapid Commit option support for the subnet.
-    subnet6->setRapidCommit(rapid_commit);
+    if (!rapid_commit.unspecified()) {
+        subnet6->setRapidCommit(rapid_commit);
+    }
 
     // Get interface-id option content. For now we support string
     // representation only
-    std::string ifaceid = getString(params, "interface-id");
-    std::string iface = getString(params, "interface");
+    Optional<std::string> ifaceid;
+    if (params->contains("interface-id")) {
+        ifaceid = getString(params, "interface-id");
+    }
+
+    Optional<std::string> iface;
+    if (params->contains("interface")) {
+        iface = getString(params, "interface");
+    }
 
     // Specifying both interface for locally reachable subnets and
     // interface id for relays is mutually exclusive. Need to test for
     // this condition.
-    if (!ifaceid.empty() && !iface.empty()) {
+    if (!ifaceid.unspecified() && !iface.unspecified() && !ifaceid.empty() &&
+        !iface.empty()) {
         isc_throw(isc::dhcp::DhcpConfigError,
                   "parser error: interface (defined for locally reachable "
                   "subnets) and interface-id (defined for subnets reachable"
@@ -1186,15 +1241,16 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
     }
 
     // Configure interface-id for remote interfaces, if defined
-    if (!ifaceid.empty()) {
-        OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
+    if (!ifaceid.unspecified() && !ifaceid.empty()) {
+        std::string ifaceid_value = ifaceid.get();
+        OptionBuffer tmp(ifaceid_value.begin(), ifaceid_value.end());
         OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
         subnet6->setInterfaceId(opt);
     }
 
     // Get interface name. If it is defined, then the subnet is available
     // directly over specified network interface.
-    if (!iface.empty()) {
+    if (!iface.unspecified() && !iface.empty()) {
         if (!IfaceMgr::instance().getIface(iface)) {
             ConstElementPtr error = params->get("interface");
             isc_throw(DhcpConfigError, "Specified network interface name " << iface
@@ -1208,33 +1264,39 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
 
     // Let's set host reservation mode. If not specified, the default value of
     // all will be used.
-    try {
-        std::string hr_mode = getString(params, "reservation-mode");
-        subnet6->setHostReservationMode(hrModeFromText(hr_mode));
-    } catch (const BadValue& ex) {
-       isc_throw(DhcpConfigError, "Failed to process specified value "
-                  " of reservation-mode parameter: " << ex.what()
-                  << "(" << getPosition("reservation-mode", params) << ")");
+    if (params->contains("reservation-mode")) {
+        try {
+            std::string hr_mode = getString(params, "reservation-mode");
+            subnet6->setHostReservationMode(hrModeFromText(hr_mode));
+        } catch (const BadValue& ex) {
+            isc_throw(DhcpConfigError, "Failed to process specified value "
+                      " of reservation-mode parameter: " << ex.what()
+                      << "(" << getPosition("reservation-mode", params) << ")");
+        }
     }
 
     // Try setting up client class.
-    string client_class = getString(params, "client-class");
-    if (!client_class.empty()) {
-        subnet6->allowClientClass(client_class);
+    if (params->contains("client-class")) {
+        string client_class = getString(params, "client-class");
+        if (!client_class.empty()) {
+            subnet6->allowClientClass(client_class);
+        }
     }
 
-    // Try setting up required client classes.
-    ConstElementPtr class_list = params->get("require-client-classes");
-    if (class_list) {
-        const std::vector<data::ElementPtr>& classes = class_list->listValue();
-        for (auto cclass = classes.cbegin();
-             cclass != classes.cend(); ++cclass) {
-            if (((*cclass)->getType() != Element::string) ||
-                (*cclass)->stringValue().empty()) {
-                isc_throw(DhcpConfigError, "invalid class name ("
-                          << (*cclass)->getPosition() << ")");
+    if (params->contains("require-client-classes")) {
+        // Try setting up required client classes.
+        ConstElementPtr class_list = params->get("require-client-classes");
+        if (class_list) {
+            const std::vector<data::ElementPtr>& classes = class_list->listValue();
+            for (auto cclass = classes.cbegin();
+                 cclass != classes.cend(); ++cclass) {
+                if (((*cclass)->getType() != Element::string) ||
+                    (*cclass)->stringValue().empty()) {
+                    isc_throw(DhcpConfigError, "invalid class name ("
+                              << (*cclass)->getPosition() << ")");
+                }
+                subnet6->requireClientClass((*cclass)->stringValue());
             }
-            subnet6->requireClientClass((*cclass)->stringValue());
         }
     }
 
index c750b6866fe0cf90256052b68249306f22a01f55..5a6725f9a7fbbf6d38318f8a66dd2e7566849b92 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -19,6 +19,8 @@
 #include <dhcpsrv/cfg_mac_source.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/option_data_parser.h>
+#include <dhcpsrv/parsers/shared_network_parser.h>
+#include <dhcpsrv/parsers/shared_networks_list_parser.h>
 #include <dhcpsrv/tests/test_libraries.h>
 #include <dhcpsrv/testutils/config_result_check.h>
 #include <exceptions/exceptions.h>
@@ -177,10 +179,11 @@ public:
     /// the parsed elements.
     ///
     /// @param config_set is the set of elements to parse.
+    /// @param v6 boolean flag indicating if this is a DHCPv6 configuration.
     /// @return returns an ConstElementPtr containing the numeric result
     /// code and outcome comment.
-    isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
-                                               config_set) {
+    isc::data::ConstElementPtr
+    parseElementSet(isc::data::ConstElementPtr config_set, bool v6) {
         // Answer will hold the result.
         ConstElementPtr answer;
         if (!config_set) {
@@ -254,6 +257,37 @@ public:
                 CfgMgr::instance().setD2ClientConfig(cfg);
             }
 
+            std::map<std::string, ConstElementPtr>::const_iterator
+                                subnets4_config = values_map.find("subnet4");
+            if (subnets4_config != values_map.end()) {
+                auto srv_config = CfgMgr::instance().getStagingCfg();
+                Subnets4ListConfigParser parser;
+                parser.parse(srv_config, subnets4_config->second);
+            }
+
+            std::map<std::string, ConstElementPtr>::const_iterator
+                                subnets6_config = values_map.find("subnet6");
+            if (subnets6_config != values_map.end()) {
+                auto srv_config = CfgMgr::instance().getStagingCfg();
+                Subnets6ListConfigParser parser;
+                parser.parse(srv_config, subnets6_config->second);
+            }
+
+            std::map<std::string, ConstElementPtr>::const_iterator
+                                networks_config = values_map.find("shared-networks");
+            if (networks_config != values_map.end()) {
+                if (v6) {
+                    auto cfg_shared_networks = CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks6();
+                    SharedNetworks6ListParser parser;
+                    parser.parse(cfg_shared_networks, networks_config->second);
+
+                } else {
+                    auto cfg_shared_networks = CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4();
+                    SharedNetworks4ListParser parser;
+                    parser.parse(cfg_shared_networks, networks_config->second);
+                }
+            }
+
             // Everything was fine. Configuration is successful.
             answer = isc::config::createAnswer(0, "Configuration committed.");
         } catch (const isc::Exception& ex) {
@@ -354,19 +388,25 @@ public:
     /// Given a configuration string, convert it into Elements
     /// and parse them.
     /// @param config is the configuration string to parse
+    /// @param v6 boolean value indicating if this is DHCPv6 configuration.
+    /// @param set_defaults boolean value indicating if the defaults should
+    /// be derived before parsing the configuration.
     ///
     /// @return returns 0 if the configuration parsed successfully,
     /// non-zero otherwise failure.
-    int parseConfiguration(const std::string& config, bool v6 = false) {
+    int parseConfiguration(const std::string& config, bool v6 = false,
+                           bool set_defaults = true) {
         int rcode_ = 1;
         // Turn config into elements.
         // Test json just to make sure its valid.
         ElementPtr json = Element::fromJSON(config);
         EXPECT_TRUE(json);
         if (json) {
-            setAllDefaults(json, v6);
+            if (set_defaults) {
+                setAllDefaults(json, v6);
+            }
 
-            ConstElementPtr status = parseElementSet(json);
+            ConstElementPtr status = parseElementSet(json, v6);
             ConstElementPtr comment = parseAnswer(rcode_, status);
             error_text_ = comment->stringValue();
             // If error was reported, the error string should contain
@@ -2314,6 +2354,230 @@ TEST_F(ParseConfigTest, bogusRelayInfo6) {
     EXPECT_THROW(parser.parse(result, json_bogus3), DhcpConfigError);
 }
 
+// This test verifies that it is possible to parse an IPv4 subnet for which
+// only mandatory parameters are specified without setting the defaults.
+TEST_F(ParseConfigTest, defaultSubnet4) {
+    std::string config =
+        "{"
+        "    \"subnet4\": [ {"
+        "        \"subnet\": \"192.0.2.0/24\","
+        "        \"id\": 123"
+        "    } ]"
+        "}";
+
+    int rcode = parseConfiguration(config, false, false);
+    ASSERT_EQ(0, rcode);
+
+    auto subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->getSubnet(123);
+    ASSERT_TRUE(subnet);
+
+    EXPECT_TRUE(subnet->getIface().unspecified());
+    EXPECT_TRUE(subnet->getIface().empty());
+
+    EXPECT_TRUE(subnet->getClientClass().unspecified());
+    EXPECT_TRUE(subnet->getClientClass().empty());
+
+    EXPECT_TRUE(subnet->getValid().unspecified());
+    EXPECT_EQ(0, subnet->getValid().get());
+
+    EXPECT_TRUE(subnet->getT1().unspecified());
+    EXPECT_EQ(0, subnet->getT1().get());
+
+    EXPECT_TRUE(subnet->getT2().unspecified());
+    EXPECT_EQ(0, subnet->getT2().get());
+
+    EXPECT_TRUE(subnet->getHostReservationMode().unspecified());
+    EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode().get());
+
+    EXPECT_TRUE(subnet->getCalculateTeeTimes().unspecified());
+    EXPECT_FALSE(subnet->getCalculateTeeTimes().get());
+
+    EXPECT_TRUE(subnet->getT1Percent().unspecified());
+    EXPECT_EQ(0.0, subnet->getT1Percent().get());
+
+    EXPECT_TRUE(subnet->getT2Percent().unspecified());
+    EXPECT_EQ(0.0, subnet->getT2Percent().get());
+
+    EXPECT_TRUE(subnet->getMatchClientId().unspecified());
+    EXPECT_TRUE(subnet->getMatchClientId().get());
+
+    EXPECT_TRUE(subnet->getAuthoritative().unspecified());
+    EXPECT_FALSE(subnet->getAuthoritative().get());
+
+    EXPECT_TRUE(subnet->getSiaddr().unspecified());
+    EXPECT_TRUE(subnet->getSiaddr().get().isV4Zero());
+
+    EXPECT_TRUE(subnet->getSname().unspecified());
+    EXPECT_TRUE(subnet->getSname().empty());
+
+    EXPECT_TRUE(subnet->getFilename().unspecified());
+    EXPECT_TRUE(subnet->getFilename().empty());
+
+    EXPECT_FALSE(subnet->get4o6().enabled());
+
+    EXPECT_TRUE(subnet->get4o6().getIface4o6().unspecified());
+    EXPECT_TRUE(subnet->get4o6().getIface4o6().empty());
+
+    EXPECT_TRUE(subnet->get4o6().getSubnet4o6().unspecified());
+    EXPECT_TRUE(subnet->get4o6().getSubnet4o6().get().first.isV6Zero());
+    EXPECT_EQ(128, subnet->get4o6().getSubnet4o6().get().second);
+}
+
+// This test verifies that it is possible to parse an IPv6 subnet for which
+// only mandatory parameters are specified without setting the defaults.
+TEST_F(ParseConfigTest, defaultSubnet6) {
+    std::string config =
+        "{"
+        "    \"subnet6\": [ {"
+        "        \"subnet\": \"2001:db8:1::/64\","
+        "        \"id\": 123"
+        "    } ]"
+        "}";
+
+    int rcode = parseConfiguration(config, true, false);
+    ASSERT_EQ(0, rcode);
+
+    auto subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getSubnet(123);
+    ASSERT_TRUE(subnet);
+
+    EXPECT_TRUE(subnet->getIface().unspecified());
+    EXPECT_TRUE(subnet->getIface().empty());
+
+    EXPECT_TRUE(subnet->getClientClass().unspecified());
+    EXPECT_TRUE(subnet->getClientClass().empty());
+
+    EXPECT_TRUE(subnet->getValid().unspecified());
+    EXPECT_EQ(0, subnet->getValid().get());
+
+    EXPECT_TRUE(subnet->getT1().unspecified());
+    EXPECT_EQ(0, subnet->getT1().get());
+
+    EXPECT_TRUE(subnet->getT2().unspecified());
+    EXPECT_EQ(0, subnet->getT2().get());
+
+    EXPECT_TRUE(subnet->getHostReservationMode().unspecified());
+    EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode().get());
+
+    EXPECT_TRUE(subnet->getCalculateTeeTimes().unspecified());
+    EXPECT_FALSE(subnet->getCalculateTeeTimes().get());
+
+    EXPECT_TRUE(subnet->getT1Percent().unspecified());
+    EXPECT_EQ(0.0, subnet->getT1Percent().get());
+
+    EXPECT_TRUE(subnet->getT2Percent().unspecified());
+    EXPECT_EQ(0.0, subnet->getT2Percent().get());
+
+    EXPECT_TRUE(subnet->getPreferred().unspecified());
+    EXPECT_EQ(0, subnet->getPreferred().get());
+
+    EXPECT_TRUE(subnet->getRapidCommit().unspecified());
+    EXPECT_FALSE(subnet->getRapidCommit().get());
+}
+
+// This test verifies that it is possible to parse an IPv4 shared network
+// for which only mandatory parameter is specified without setting the
+// defaults.
+TEST_F(ParseConfigTest, defaultSharedNetwork4) {
+    std::string config =
+        "{"
+        "    \"shared-networks\": [ {"
+        "        \"name\": \"frog\""
+        "    } ]"
+        "}";
+
+    int rcode = parseConfiguration(config, false, false);
+    ASSERT_EQ(0, rcode);
+
+    auto network =
+        CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->getByName("frog");
+    ASSERT_TRUE(network);
+
+    EXPECT_TRUE(network->getIface().unspecified());
+    EXPECT_TRUE(network->getIface().empty());
+
+    EXPECT_TRUE(network->getClientClass().unspecified());
+    EXPECT_TRUE(network->getClientClass().empty());
+
+    EXPECT_TRUE(network->getValid().unspecified());
+    EXPECT_EQ(0, network->getValid().get());
+
+    EXPECT_TRUE(network->getT1().unspecified());
+    EXPECT_EQ(0, network->getT1().get());
+
+    EXPECT_TRUE(network->getT2().unspecified());
+    EXPECT_EQ(0, network->getT2().get());
+
+    EXPECT_TRUE(network->getHostReservationMode().unspecified());
+    EXPECT_EQ(Network::HR_ALL, network->getHostReservationMode().get());
+
+    EXPECT_TRUE(network->getCalculateTeeTimes().unspecified());
+    EXPECT_FALSE(network->getCalculateTeeTimes().get());
+
+    EXPECT_TRUE(network->getT1Percent().unspecified());
+    EXPECT_EQ(0.0, network->getT1Percent().get());
+
+    EXPECT_TRUE(network->getT2Percent().unspecified());
+    EXPECT_EQ(0.0, network->getT2Percent().get());
+
+    EXPECT_TRUE(network->getMatchClientId().unspecified());
+    EXPECT_TRUE(network->getMatchClientId().get());
+
+    EXPECT_TRUE(network->getAuthoritative().unspecified());
+    EXPECT_FALSE(network->getAuthoritative().get());
+}
+
+// This test verifies that it is possible to parse an IPv6 shared network
+// for which only mandatory parameter is specified without setting the
+// defaults.
+TEST_F(ParseConfigTest, defaultSharedNetwork6) {
+    std::string config =
+        "{"
+        "    \"shared-networks\": [ {"
+        "        \"name\": \"frog\""
+        "    } ]"
+        "}";
+
+    int rcode = parseConfiguration(config, true, false);
+    ASSERT_EQ(0, rcode);
+
+    auto network =
+        CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks6()->getByName("frog");
+    ASSERT_TRUE(network);
+
+    EXPECT_TRUE(network->getIface().unspecified());
+    EXPECT_TRUE(network->getIface().empty());
+
+    EXPECT_TRUE(network->getClientClass().unspecified());
+    EXPECT_TRUE(network->getClientClass().empty());
+
+    EXPECT_TRUE(network->getValid().unspecified());
+    EXPECT_EQ(0, network->getValid().get());
+
+    EXPECT_TRUE(network->getT1().unspecified());
+    EXPECT_EQ(0, network->getT1().get());
+
+    EXPECT_TRUE(network->getT2().unspecified());
+    EXPECT_EQ(0, network->getT2().get());
+
+    EXPECT_TRUE(network->getHostReservationMode().unspecified());
+    EXPECT_EQ(Network::HR_ALL, network->getHostReservationMode().get());
+
+    EXPECT_TRUE(network->getCalculateTeeTimes().unspecified());
+    EXPECT_FALSE(network->getCalculateTeeTimes().get());
+
+    EXPECT_TRUE(network->getT1Percent().unspecified());
+    EXPECT_EQ(0.0, network->getT1Percent().get());
+
+    EXPECT_TRUE(network->getT2Percent().unspecified());
+    EXPECT_EQ(0.0, network->getT2Percent().get());
+
+    EXPECT_TRUE(network->getPreferred().unspecified());
+    EXPECT_EQ(0, network->getPreferred().get());
+
+    EXPECT_TRUE(network->getRapidCommit().unspecified());
+    EXPECT_FALSE(network->getRapidCommit().get());
+}
+
 // There's no test for ControlSocketParser, as it is tested in the DHCPv4 code
 // (see CtrlDhcpv4SrvTest.commandSocketBasic in
 // src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc).