"valid-lifetime": 600,
"subnet4": [ {
"subnet": "10.0.0.0/24",
- "reservation-mode": "global",
+ # It is deprecated by the "reservation-modes" map.
+ # "reservation-mode": "global",
+ # Reservation modes specifying server's mode of operation when it
+ # fetches host reservations.
+ "reservation-modes": {
+ "global": True,
+ "in-subnet": False,
+ "out-of-pool": False
+ },
"pools": [ { "pool": "10.0.0.10-10.0.0.100" } ]
} ]
}
"hw-address": "aa:bb:cc:dd:ee:fe",
"client-classes": [ "reserved_class" ]
}],
- "reservation-mode": "global",
+ # It is deprecated by the "reservation-modes" map.
+ # "reservation-mode": "global",
+ # Reservation modes specifying server's mode of operation when it
+ # fetches host reservations.
+ "reservation-modes": {
+ "global": True,
+ "in-subnet": False,
+ "out-of-pool": False
+ },
"shared-networks": [{
"subnet4": [
{
"valid-lifetime": 600,
"subnet4": [ {
"subnet": "2001:db8:1::/64",
- "reservation-mode": "global",
+ # It is deprecated by the "reservation-modes" map.
+ # "reservation-mode": "global",
+ # Reservation modes specifying server's mode of operation when it
+ # fetches host reservations.
+ "reservation-modes": {
+ "global": True,
+ "in-subnet": False,
+ "out-of-pool": False
+ },
"pools": [ { "pool": "2001:db8:1::-2001:db8:1::100" } ]
} ]
}
"hw-address": "aa:bb:cc:dd:ee:fe",
"client-classes": [ "reserved_class" ]
}],
- "reservation-mode": "global",
+ # It is deprecated by the "reservation-modes" map.
+ # "reservation-mode": "global",
+ # Reservation modes specifying server's mode of operation when it
+ # fetches host reservations.
+ "reservation-modes": {
+ "global": True,
+ "in-subnet": False,
+ "out-of-pool": False
+ },
"shared-networks": [{
"subnet6": [
{
"ip-address": "0.0.0.0"
},
"renew-timer": 60,
- "reservation-mode": "all",
+ # It is deprecated by the "reservation-modes" map.
+ # "reservation-mode": "all",
+ # Reservation modes specifying server's mode of operation when it
+ # fetches host reservations.
+ "reservation-modes": {
+ "global": False,
+ "in-subnet": True,
+ "out-of-pool": True
+ },
"subnet4": [
{
"subnet": "192.0.2.0/24",
}
}
+ ConstElementPtr reservation_mode = mutable_cfg->get("reservation-mode");
+ if (reservation_mode) {
+ reservation_mode = mutable_cfg->get("reservation-modes");
+ if (reservation_mode) {
+ isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
+ " and 'reservation-modes' parameters");
+ }
+ }
+
ConstElementPtr config_control = mutable_cfg->get("config-control");
if (config_control) {
parameter_name = "config-control";
(config_pair.first == "boot-file-name") ||
(config_pair.first == "server-tag") ||
(config_pair.first == "reservation-mode") ||
+ (config_pair.first == "reservation-modes") ||
(config_pair.first == "calculate-tee-times") ||
(config_pair.first == "t1-percent") ||
(config_pair.first == "t2-percent") ||
return ("subnet4");
case RESERVATION_MODE:
return ("reservation-mode");
+ case RESERVATION_MODES:
+ return ("reservation-modes");
case OPTION_DEF:
return ("option-def");
case OPTION_DATA:
/// This will parse the input as hooks-library.
PARSER_HOOKS_LIBRARY,
- /// This will parse the input as dhcp-ddns.
+ /// This will parse the input as dhcp-ddns. (D2 client config)
PARSER_DHCP_DDNS,
/// This will parse the input as reservation-modes.
/// Check if a required parameter is present in the map at the top
/// of the stack and raise an error when it is not.
///
- /// @param name name of the parameter to check
+ /// @param name name of the parameter expected to be present
/// @param open_loc location of the opening curly bracket
/// @param close_loc location of the closing curly bracket
/// @throw Dhcp4ParseError
}
}
+ ConstElementPtr reservation_mode = mutable_cfg->get("reservation-mode");
+ if (reservation_mode) {
+ reservation_mode = mutable_cfg->get("reservation-modes");
+ if (reservation_mode) {
+ isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
+ " and 'reservation-modes' parameters");
+ }
+ }
+
ConstElementPtr config_control = mutable_cfg->get("config-control");
if (config_control) {
parameter_name = "config-control";
(config_pair.first == "dhcp4o6-port") ||
(config_pair.first == "server-tag") ||
(config_pair.first == "reservation-mode") ||
+ (config_pair.first == "reservation-modes") ||
(config_pair.first == "calculate-tee-times") ||
(config_pair.first == "t1-percent") ||
(config_pair.first == "t2-percent") ||
return ("subnet6");
case RESERVATION_MODE:
return ("reservation-mode");
+ case RESERVATION_MODES:
+ return ("reservation-modes");
case OPTION_DEF:
return ("option-def");
case OPTION_DATA:
// specific options.
//
// When using reservations, it is useful to configure
- // reservation-mode (subnet specific parameter) and
+ // reservation-modes (subnet specific parameter) and
// host-reservation-identifiers (global parameter).
{
"client-id": "01:12:23:34:45:56:67",
EXTRA_DIST += parsers/multi_threading_config_parser.cc
EXTRA_DIST += parsers/multi_threading_config_parser.h
EXTRA_DIST += parsers/option_data_parser.h
+EXTRA_DIST += parsers/reservation_modes_parser.cc
+EXTRA_DIST += parsers/reservation_modes_parser.h
EXTRA_DIST += parsers/sanity_checks_parser.cc
EXTRA_DIST += parsers/sanity_checks_parser.h
EXTRA_DIST += parsers/simple_parser4.cc
libkea_dhcpsrv_la_SOURCES += parsers/option_data_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/dhcp_queue_control_parser.cc
libkea_dhcpsrv_la_SOURCES += parsers/dhcp_queue_control_parser.h
+libkea_dhcpsrv_la_SOURCES += parsers/reservation_modes_parser.cc
+libkea_dhcpsrv_la_SOURCES += parsers/reservation_modes_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/sanity_checks_parser.cc
libkea_dhcpsrv_la_SOURCES += parsers/sanity_checks_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/shared_network_parser.cc
parsers/multi_threading_config_parser.h \
parsers/option_data_parser.h \
parsers/dhcp_queue_control_parser.h \
+ parsers/reservation_modes_parser.h \
parsers/sanity_checks_parser.h \
parsers/shared_network_parser.h \
parsers/shared_networks_list_parser.h \
/// @brief Specifies allowed host reservation mode.
///
- typedef enum {
+ typedef enum : uint8_t {
/// None - host reservation is disabled. No reservation types
/// are allowed.
/// AllocEngine code has to check whether there are reservations, even
/// when dealing with reservations from within the dynamic pools.
HR_ALL = HR_IN_SUBNET | HR_OUT_OF_POOL
- } HRMode;
+ } HRModeFlag;
+
+ /// @brief Bitset used to store @ref HRModeFlag flags.
+ typedef uint8_t HRMode;
/// @brief Inheritance "mode" used when fetching an optional @c Network
/// parameter.
template<typename ReturnType>
ReturnType getGlobalProperty(ReturnType property,
const std::string& global_name) const {
- if (!global_name.empty() && fetch_globals_fn_) {
+ std::string member_name;
+ std::string search_name = global_name;
+ auto found = global_name.find('.');
+ if (found != std::string::npos) {
+ if (std::count(global_name.begin(), global_name.end(), '.') > 1) {
+ isc_throw(BadValue, "more than one level of indirection found in: "
+ << global_name);
+ }
+ member_name = global_name.substr(found + 1, global_name.length() - found - 1);
+ search_name = global_name.substr(0, found);
+ }
+ if (!search_name.empty() && fetch_globals_fn_) {
data::ConstElementPtr globals = fetch_globals_fn_();
if (globals && (globals->getType() == data::Element::map)) {
- data::ConstElementPtr global_param = globals->get(global_name);
+ data::ConstElementPtr global_param = globals->get(search_name);
if (global_param) {
- // If there is a global parameter, convert it to the
- // optional value of the given type and return.
- return (data::ElementValue<typename ReturnType::ValueType>()(global_param));
+ if (!member_name.empty()) {
+ if (global_param->getType() != data::Element::map) {
+ isc_throw(BadValue, "the parameter: " << global_name
+ << " must be a map");
+ }
+ auto member_element = global_param->get(member_name);
+ if (member_element) {
+ // If there is a global parameter with the specified
+ // member, convert the member to the optional value
+ // of the given type and return.
+ return (data::ElementValue<typename ReturnType::ValueType>()(member_element));
+ }
+ } else {
+ // If there is a global parameter, convert it to the
+ // optional value of the given type and return.
+ return (data::ElementValue<typename ReturnType::ValueType>()(global_param));
+ }
}
}
}
#include <config.h>
#include <dhcpsrv/triplet.h>
#include <dhcpsrv/parsers/base_network_parser.h>
+#include <dhcpsrv/parsers/reservation_modes_parser.h>
#include <util/optional.h>
#include <util/strutil.h>
BaseNetworkParser::parseHostReservationMode(const data::ConstElementPtr& network_data,
NetworkPtr& network) {
if (network_data->contains("reservation-mode")) {
+ if (network_data->contains("reservation-modes")) {
+ isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
+ " and 'reservation-modes' parameters");
+ }
try {
std::string hr_mode = getString(network_data, "reservation-mode");
network->setHostReservationMode(Network::hrModeFromString(hr_mode));
}
}
+void
+BaseNetworkParser::parseHostReservationModes(const data::ConstElementPtr& network_data,
+ NetworkPtr& network) {
+ if (network_data->contains("reservation-modes")) {
+ if (network_data->contains("reservation-mode")) {
+ isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
+ " and 'reservation-modes' parameters");
+ }
+ try {
+ auto reservation_modes = network_data->get("reservation-modes");
+ HostReservationModesParser parser;
+ Network::HRMode flags = parser.parse(reservation_modes);
+ network->setHostReservationMode(flags);
+ } catch (const BadValue& ex) {
+ isc_throw(DhcpConfigError, "invalid reservation-modes parameter: "
+ << ex.what() << " (" << getPosition("reservation-modes",
+ network_data) << ")");
+ }
+ }
+}
+
void
BaseNetworkParser::parseDdnsParams(const data::ConstElementPtr& network_data,
NetworkPtr& network) {
/// @brief Parses host reservation mode.
///
+ /// @note Configuring 'reservation-mode' is deprecated. The new map
+ /// 'reservation-modes' should be used.
+ ///
/// @param network_data Data element holding shared network
/// configuration to be parsed.
/// @param [out] network Pointer to a network in which parsed data is
void parseHostReservationMode(const data::ConstElementPtr& network_data,
NetworkPtr& network);
+ /// @brief Parses host reservation modes.
+ ///
+ /// @param network_data Data element holding shared network
+ /// configuration to be parsed.
+ /// @param [out] network Pointer to a network in which parsed data is
+ /// to be stored.
+ void parseHostReservationModes(const data::ConstElementPtr& network_data,
+ NetworkPtr& network);
+
/// @brief Parses parameters pertaining to DDNS behavior.
///
/// The parsed parameters are:
}
}
- // Let's set host reservation mode.
+ // reservation-modes
+ parseHostReservationModes(params, network);
+
+ // Let's set host reservation mode. If not specified, the default value of
+ // all will be used.
parseHostReservationMode(params, network);
// Try setting up client class.
subnet6->setIface(iface);
}
+ // reservation-modes
+ parseHostReservationModes(params, network);
+
// Let's set host reservation mode. If not specified, the default value of
// all will be used.
parseHostReservationMode(params, network);
--- /dev/null
+// Copyright (C) 2015-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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <cc/data.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/parsers/reservation_modes_parser.h>
+
+#include <string>
+#include <sys/types.h>
+
+using namespace isc::data;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+Network::HRMode
+HostReservationModesParser::parse(const ConstElementPtr& control_elem) {
+ if (control_elem->getType() != Element::map) {
+ isc_throw(DhcpConfigError, "reservation-modes must be a map");
+ }
+
+ ConstElementPtr elem;
+ uint8_t flags = 0;
+
+ elem = control_elem->get("global");
+ if (elem) {
+ bool value = elem->boolValue();
+ if (value) {
+ flags |= Network::HR_GLOBAL;
+ }
+ }
+
+ elem = control_elem->get("in-subnet");
+ if (elem) {
+ bool value = elem->boolValue();
+ if (value) {
+ flags |= Network::HR_IN_SUBNET;
+ }
+ }
+
+ elem = control_elem->get("out-of-pool");
+ if (elem) {
+ bool value = elem->boolValue();
+ if (value) {
+ flags |= Network::HR_OUT_OF_POOL;
+ }
+ }
+
+ return (static_cast<Network::HRMode>(flags));
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 2018 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef RESERVATION_MODES_PARSER_H
+#define RESERVATION_MODES_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/network.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for the configuration of DHCP packet queue controls
+///
+/// This parser parses the "reservation-modes" parameter which holds the
+/// the configurable parameters that tailor host reservation modes.
+///
+/// This parser is used in both DHCPv4 and DHCPv6, and also inside subnet and
+/// shared networks.
+class HostReservationModesParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Constructor
+ ///
+ HostReservationModesParser(){};
+
+ /// @brief Parses content of the "reservation-modes".
+ ///
+ /// @param control_elem MapElement containing the host reservation modes
+ /// values to parse
+ ///
+ /// @return Host reservation modes flags.
+ ///
+ /// @throw DhcpConfigError if any of the values are invalid.
+ Network::HRMode parse(const isc::data::ConstElementPtr& control_elem);
+};
+
+}
+} // end of namespace isc
+
+#endif // RESERVATION_MODES_PARSER_H
}
}
+ // reservation-modes
+ parseHostReservationModes(shared_network_data, network);
+
// reservation-mode
parseHostReservationMode(shared_network_data, network);
}
}
+ // reservation-modes
+ parseHostReservationModes(shared_network_data, network);
+
// reservation-mode
parseHostReservationMode(shared_network_data, network);
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
libdhcpsrv_unittests_SOURCES += dhcp_queue_control_parser_unittest.cc
+libdhcpsrv_unittests_SOURCES += reservation_modes_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += dhcp4o6_ipc_unittest.cc
libdhcpsrv_unittests_SOURCES += duid_config_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += expiration_config_parser_unittest.cc
}
// Verifies that DHCPQueueControlParser handles
-// expected valid dhcp-queue-control contet
+// expected valid dhcp-queue-control content
TEST_F(DHCPQueueControlParserTest, validContent) {
struct Scenario {
std::string description_;
}
};
- // Iterate over the valid scenarios and verify they succeed.
+ // Iterate over the invalid scenarios and verify they throw exception.
ConstElementPtr config_elems;
- ConstElementPtr queue_control;
for (auto scenario : scenarios) {
SCOPED_TRACE(scenario.description_);
{
--- /dev/null
+// Copyright (C) 2018-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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/data.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/reservation_modes_parser.h>
+#include <testutils/test_to_element.h>
+#include <gtest/gtest.h>
+
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::test;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Test fixture class for @c HostReservationModesParser
+class HostReservationModesParserTest : public ::testing::Test {
+protected:
+
+ /// @brief Setup for each test.
+ ///
+ /// Clears the configuration in the @c CfgMgr.
+ virtual void SetUp();
+
+ /// @brief Cleans up after each test.
+ ///
+ /// Clears the configuration in the @c CfgMgr.
+ virtual void TearDown();
+
+};
+
+void
+HostReservationModesParserTest::SetUp() {
+ CfgMgr::instance().clear();
+}
+
+void
+HostReservationModesParserTest::TearDown() {
+ CfgMgr::instance().clear();
+}
+
+// Verifies that HostReservationModesParser handles
+// expected valid reservation-modes content
+TEST_F(HostReservationModesParserTest, validContent) {
+ struct Scenario {
+ std::string description_;
+ std::string json_;
+ };
+
+ std::vector<Scenario> scenarios = {
+ {
+ "queue disabled",
+ "{ \n"
+ " \"enable-queue\": false \n"
+ "} \n"
+ },
+ {
+ "queue disabled, arbitrary content allowed",
+ "{ \n"
+ " \"enable-queue\": false, \n"
+ " \"foo\": \"bogus\", \n"
+ " \"random-int\" : 1234 \n"
+ "} \n"
+ },
+ {
+ "queue enabled, with queue-type",
+ "{ \n"
+ " \"enable-queue\": true, \n"
+ " \"queue-type\": \"some-type\" \n"
+ "} \n"
+ },
+ {
+ "queue enabled with queue-type and arbitrary content",
+ "{ \n"
+ " \"enable-queue\": true, \n"
+ " \"queue-type\": \"some-type\", \n"
+ " \"foo\": \"bogus\", \n"
+ " \"random-int\" : 1234 \n"
+ "} \n"
+ }
+ };
+
+ // Iterate over the valid scenarios and verify they succeed.
+ ConstElementPtr config_elems;
+ Network::HRMode reservation_modes;
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+ {
+ // Construct the config JSON
+ ASSERT_NO_THROW(config_elems = Element::fromJSON(scenario.json_))
+ << "invalid JSON, test is broken";
+
+ // Parsing config into a reservation modes parser should succeed.
+ HostReservationModesParser parser;
+ try {
+ reservation_modes = parser.parse(config_elems);
+ } catch (const std::exception& ex) {
+ ADD_FAILURE() << "parser threw an exception: " << ex.what();
+ }
+
+ // Verify the resultant reservation-modes.
+ ASSERT_TRUE(reservation_modes);
+ }
+ }
+}
+
+// Verifies that HostReservationModesParser correctly catches
+// invalid reservation-modes content
+TEST_F(HostReservationModesParserTest, invalidContent) {
+ struct Scenario {
+ std::string description_;
+ std::string json_;
+ };
+
+ std::vector<Scenario> scenarios = {
+ {
+ "enable-queue missing",
+ "{ \n"
+ " \"enable-type\": \"some-type\" \n"
+ "} \n"
+ },
+ {
+ "enable-queue not boolean",
+ "{ \n"
+ " \"enable-queue\": \"always\" \n"
+ "} \n"
+ },
+ {
+ "queue enabled, type missing",
+ "{ \n"
+ " \"enable-queue\": true \n"
+ "} \n"
+ },
+ {
+ "queue enabled, type not a string",
+ "{ \n"
+ " \"enable-queue\": true, \n"
+ " \"queue-type\": 7777 \n"
+ "} \n"
+ }
+ };
+
+ // Iterate over the invalid scenarios and verify they throw exception.
+ ConstElementPtr config_elems;
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+ {
+ // Construct the config JSON
+ ASSERT_NO_THROW(config_elems = Element::fromJSON(scenario.json_))
+ << "invalid JSON, test is broken";
+
+ // Parsing config into a reservation modes parser should succeed.
+ HostReservationModesParser parser;
+ EXPECT_THROW(parser.parse(config_elems), DhcpConfigError);
+ }
+ }
+}
+
+}; // anonymous namespace