]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1428] Basic support for ip-reservations-unique
authorMarcin Siodelski <marcin@isc.org>
Fri, 2 Oct 2020 15:36:31 +0000 (17:36 +0200)
committerFrancis Dupont <fdupont@isc.org>
Thu, 8 Oct 2020 13:44:39 +0000 (15:44 +0200)
The DHCPv4 and DHCPv6 servers accept ip-reservations-unique configuration
for IP addresses specified in the configuration file.

src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp4/tests/host_unittest.cc
src/bin/dhcp6/json_config_parser.cc
src/bin/dhcp6/tests/host_unittest.cc
src/lib/dhcpsrv/cfg_db_access.h
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/srv_config.h

index 2a3df4f5b1ff0d35b26a147ffa329f78ba1b7c38..8610cbaea25044b3e81254af10fc81ad602e8b36 100644 (file)
@@ -116,6 +116,23 @@ public:
         cfg->setServerTag(server_tag);
     }
 
+    /// @brief Sets global parameters before other parameters are parsed.
+    ///
+    /// This method sets selected global parameters before other parameters
+    /// are parsed. This is important when the bahavior of the parsers
+    /// run later depends on these global paramters.
+    ///
+    /// Currently this method sets the following global parameters:
+    /// - ip-reservations-unique
+    ///
+    /// @param global global configuration scope
+    /// @param cfg Server configuration (parsed parameters will be stored here)
+    void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
+        // Set ip-reservations-unique flag.
+        bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
+        cfg->setIPReservationsUnique(ip_reservations_unique);
+    }
+
     /// @brief Copies subnets from shared networks to regular subnets container
     ///
     /// @param from pointer to shared networks container (copy from here)
@@ -368,6 +385,12 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         // with the parser code debugability, so I decided to keep it as a
         // series of independent ifs.
 
+        // This parser is used in several places.
+        Dhcp4ConfigParser global_parser;
+
+        // Apply global options in the staging config, e.g. ip-reservations-unique
+        global_parser.parseEarly(srv_cfg, mutable_cfg);
+
         // We need definitions first
         ConstElementPtr option_defs = mutable_cfg->get("option-def");
         if (option_defs) {
@@ -504,9 +527,6 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
             }
         }
 
-        // This parser is used in several places.
-        Dhcp4ConfigParser global_parser;
-
         // Keep relative orders of shared networks and subnets.
         ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
         if (shared_networks) {
index de109c606c480afc5f85d33c00fecdfb6144e4c4..92fc30a6968c7dce7b7012ca6b63df04062ca7a6 100644 (file)
@@ -306,6 +306,36 @@ const char* CONFIGS[] = {
         "        \"interface\": \"eth0\"\n"
         "    }\n"
         "]\n"
+    "}",
+
+    // Configuration 7 multiple reservations for the same IP address.
+    "{ \"interfaces-config\": {\n"
+        "      \"interfaces\": [ \"*\" ]\n"
+        "},\n"
+        "\"valid-lifetime\": 600,\n"
+        "\"ip-reservations-unique\": false,\n"
+        "\"subnet4\": [\n"
+        "    {\n"
+        "        \"subnet\": \"10.0.0.0/24\",\n"
+        "        \"id\": 10,\n"
+        "        \"reservations\": [\n"
+        "            { \n"
+        "                \"hw-address\": \"aa:bb:cc:dd:ee:fe\",\n"
+        "                \"ip-address\": \"10.0.0.123\"\n"
+        "            },\n"
+        "            { \n"
+        "                \"hw-address\": \"aa:bb:cc:dd:ee:ff\",\n"
+        "                \"ip-address\": \"10.0.0.123\"\n"
+        "            }\n"
+        "        ],\n"
+        "        \"pools\": [\n"
+        "            {\n"
+        "                \"pool\": \"10.0.0.10-10.0.0.255\"\n"
+        "            }\n"
+        "        ],\n"
+        "        \"interface\": \"eth0\"\n"
+        "    }\n"
+        "]\n"
     "}"
 };
 
@@ -589,4 +619,16 @@ TEST_F(HostTest, clientClassPoolSelection) {
     ASSERT_NO_FATAL_FAILURE(testGlobalClassSubnetPoolSelection(6, "10.0.0.10", "10.0.0.20"));
 }
 
+// Verifies that if the server is configured to allow for specifying
+// multiple reservations for the same IP address the first client
+// matching the reservation will be given this address.
+TEST_F(HostTest, oneOfMultiple) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Hardware address matches all reservations
+    client.setHWAddress("aa:bb:cc:dd:ee:fe");
+
+    runDoraTest(CONFIGS[7], client, "", "10.0.0.123");
+}
+
 } // end of anonymous namespace
index e72d406fd5cfc9f6451aeb75ff610edd0e2bd6bf..565de4af183fa9b1e4d0f49f9169b047af945e8f 100644 (file)
@@ -203,6 +203,23 @@ public:
         srv_config->setServerTag(server_tag);
     }
 
+    /// @brief Sets global parameters before other parameters are parsed.
+    ///
+    /// This method sets selected global parameters before other parameters
+    /// are parsed. This is important when the bahavior of the parsers
+    /// run later depends on these global paramters.
+    ///
+    /// Currently this method sets the following global parameters:
+    /// - ip-reservations-unique
+    ///
+    /// @param global global configuration scope
+    /// @param cfg Server configuration (parsed parameters will be stored here)
+    void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
+        // Set ip-reservations-unique flag.
+        bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
+        cfg->setIPReservationsUnique(ip_reservations_unique);
+    }
+
     /// @brief Copies subnets from shared networks to regular subnets container
     ///
     /// @param from pointer to shared networks container (copy from here)
@@ -469,6 +486,12 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
         // with the parser code debugability, so I decided to keep it as a
         // series of independent ifs.
 
+        // This parser is used in several places.
+        Dhcp6ConfigParser global_parser;
+
+        // Apply global options in the staging config, e.g. ip-reservations-unique
+        global_parser.parseEarly(srv_config, mutable_cfg);
+
         // Specific check for this global parameter.
         ConstElementPtr data_directory = mutable_cfg->get("data-directory");
         if (data_directory) {
@@ -628,9 +651,6 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
             }
         }
 
-        // This parser is used in several places.
-        Dhcp6ConfigParser global_parser;
-
         // Keep relative orders of shared networks and subnets.
         ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
         if (shared_networks) {
index c3ff65a9fc99701db426113fdf937a41450050f8..85cb81467ed918646088f7daf6a1ea8d0b223ae3 100644 (file)
@@ -559,6 +559,36 @@ const char* CONFIGS[] = {
         "        \"interface\": \"eth0\"\n"
         "    }\n"
         "]\n"
+    "}",
+
+    // Configuration 13 multiple reservations for the same IP address.
+    "{ \"interfaces-config\": {\n"
+        "      \"interfaces\": [ \"*\" ]\n"
+        "},\n"
+        "\"valid-lifetime\": 4000,\n"
+        "\"ip-reservations-unique\": false,\n"
+        "\"subnet6\": [\n"
+        "    {\n"
+        "        \"subnet\": \"2001:db8:1::/64\",\n"
+        "        \"id\": 10,"
+        "        \"reservations\": [\n"
+        "            {\n"
+        "                \"duid\": \"01:02:03:04\",\n"
+        "                \"ip-addresses\": [ \"2001:db8:1::15\" ]\n"
+        "            },\n"
+        "            {\n"
+        "                \"duid\": \"01:02:03:05\",\n"
+        "                \"ip-addresses\": [ \"2001:db8:1::15\" ]\n"
+        "            }\n"
+        "        ],\n"
+        "        \"pools\": ["
+        "            {\n"
+        "                \"pool\": \"2001:db8:1::10-2001:db8:1::200\""
+        "            }\n"
+        "        ],\n"
+        "        \"interface\": \"eth0\"\n"
+        "    }\n"
+        "]\n"
     "}"
 };
 
@@ -2357,4 +2387,23 @@ TEST_F(HostTest, clientClassPoolSelection) {
                                                                "2001:db8:1::20"));
 }
 
+// Verifies that if the server is configured to allow for specifying
+// multiple reservations for the same IP address the first client
+// matching the reservation will be given this address.
+TEST_F(HostTest, oneOfMultiple) {
+    Dhcp6Client client1;
+    client1.setDUID("01:02:03:04");
+
+    ASSERT_NO_THROW(configure(CONFIGS[13], *client1.getServer()));
+
+    // First client performs 4-way exchange and obtains an address and
+    // prefix indicated in hints.
+    requestIA(client1, Hint(IAID(1), "2001:db8:1::10"));
+
+    ASSERT_NO_THROW(client1.doSARR());
+
+    // Make sure the client has obtained requested leases.
+    ASSERT_TRUE(client1.hasLeaseForAddress(IOAddress("2001:db8:1::15"), IAID(1)));
+}
+
 } // end of anonymous namespace
index b4ac00c24572348939c7f3ad8bf28961c3df9ff3..e19f04de5947a5d71dc261c91e0f1345dcdca115 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2020 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
@@ -74,6 +74,23 @@ public:
     /// specified with @ref CfgDbAccess::setAppendedParameters
     std::list<std::string> getHostDbAccessStringList() const;
 
+    /// @brief Modifies the setting imposing whether the IP reservations
+    /// are unique or can be non-unique.
+    ///
+    /// This flag can be set to @c false when the server is explicitly
+    /// configured to allow multiple hosts to have the same IP reservation.
+    /// In that case, the @c createManagers function will attempt to use
+    /// this setting for @c HostMgr.
+    ///
+    /// Note that the @c HostMgr can reject the new setting if any of the
+    /// host backends used does not support specifying multipe hosts with
+    /// the same IP address.
+    ///
+    /// @param unique new setting to be used by @c HostMgr.
+    void setIPReservationsUnique(const bool unique) {
+        ip_reservations_unique_ = unique;
+    }
+
     /// @brief Creates instance of lease manager and host data sources
     /// according to the configuration specified.
     void createManagers() const;
@@ -95,6 +112,9 @@ protected:
     /// @brief Holds host database access strings.
     std::list<std::string> host_db_access_;
 
+    /// @brief Holds the setting whether IP reservations should be unique
+    /// or can be non-unique.
+    bool ip_reservations_unique_;
 };
 
 /// @brief A pointer to the @c CfgDbAccess.
index 6b14cfbe1cca54e5efe85dcdaf387f9c5b3a90c3..981c62905008fd7ca18e24c9097db1db3ba03da6 100644 (file)
@@ -680,6 +680,12 @@ SrvConfig::moveDdnsParams(isc::data::ElementPtr srv_elem) {
     }
 }
 
+void
+SrvConfig::setIPReservationsUnique(const bool unique) {
+    getCfgHosts()->setIPReservationsUnique(unique);
+    getCfgDbAccess()->setIPReservationsUnique(unique);
+}
+
 bool
 DdnsParams::getEnableUpdates() const {
     if (!subnet_) {
index 036fef9d95cf58dd76aba3bd95080890f96a71f7..9c0f153c6d9f8e07897d4a5a0af6bb1564d35435 100644 (file)
@@ -800,6 +800,17 @@ public:
     /// @param srv_elem server top level map to alter
     static void moveDdnsParams(isc::data::ElementPtr srv_elem);
 
+    /// @brief Configures the server to allow or disallow specifying multiple
+    /// hosts with the same IP address.
+    ///
+    /// This setting is applied in @c CfgDbAccess and @c CfgHosts. This function
+    /// should be called when the server is being configured using the configuration
+    /// file, config-set command or via the configuration backend.
+    ///
+    /// @param unique Boolean value indicating if it is allowed (when false)
+    /// or disallowed to specify multiple hosts with the same IP reservation.
+    void setIPReservationsUnique(const bool unique);
+
     /// @brief Unparse a configuration object
     ///
     /// @return a pointer to unparsed configuration