From: Marcin Siodelski Date: Fri, 2 Oct 2020 15:36:31 +0000 (+0200) Subject: [#1428] Basic support for ip-reservations-unique X-Git-Tag: Kea-1.9.1~112 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ab278ed455bd2c80d144bcd4a53eb3d9830b4f99;p=thirdparty%2Fkea.git [#1428] Basic support for ip-reservations-unique The DHCPv4 and DHCPv6 servers accept ip-reservations-unique configuration for IP addresses specified in the configuration file. --- diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 2a3df4f5b1..8610cbaea2 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -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) { diff --git a/src/bin/dhcp4/tests/host_unittest.cc b/src/bin/dhcp4/tests/host_unittest.cc index de109c606c..92fc30a696 100644 --- a/src/bin/dhcp4/tests/host_unittest.cc +++ b/src/bin/dhcp4/tests/host_unittest.cc @@ -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 diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index e72d406fd5..565de4af18 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -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) { diff --git a/src/bin/dhcp6/tests/host_unittest.cc b/src/bin/dhcp6/tests/host_unittest.cc index c3ff65a9fc..85cb81467e 100644 --- a/src/bin/dhcp6/tests/host_unittest.cc +++ b/src/bin/dhcp6/tests/host_unittest.cc @@ -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 diff --git a/src/lib/dhcpsrv/cfg_db_access.h b/src/lib/dhcpsrv/cfg_db_access.h index b4ac00c245..e19f04de59 100644 --- a/src/lib/dhcpsrv/cfg_db_access.h +++ b/src/lib/dhcpsrv/cfg_db_access.h @@ -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 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 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. diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index 6b14cfbe1c..981c629050 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -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_) { diff --git a/src/lib/dhcpsrv/srv_config.h b/src/lib/dhcpsrv/srv_config.h index 036fef9d95..9c0f153c6d 100644 --- a/src/lib/dhcpsrv/srv_config.h +++ b/src/lib/dhcpsrv/srv_config.h @@ -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