summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/parsers
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/parsers')
-rw-r--r--src/lib/dhcpsrv/parsers/base_network_parser.cc324
-rw-r--r--src/lib/dhcpsrv/parsers/base_network_parser.h151
-rw-r--r--src/lib/dhcpsrv/parsers/client_class_def_parser.cc358
-rw-r--r--src/lib/dhcpsrv/parsers/client_class_def_parser.h175
-rw-r--r--src/lib/dhcpsrv/parsers/dhcp_parsers.cc1676
-rw-r--r--src/lib/dhcpsrv/parsers/dhcp_parsers.h1079
-rw-r--r--src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.cc61
-rw-r--r--src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.h58
-rw-r--r--src/lib/dhcpsrv/parsers/duid_config_parser.cc95
-rw-r--r--src/lib/dhcpsrv/parsers/duid_config_parser.h41
-rw-r--r--src/lib/dhcpsrv/parsers/expiration_config_parser.cc69
-rw-r--r--src/lib/dhcpsrv/parsers/expiration_config_parser.h59
-rw-r--r--src/lib/dhcpsrv/parsers/host_reservation_parser.cc457
-rw-r--r--src/lib/dhcpsrv/parsers/host_reservation_parser.h243
-rw-r--r--src/lib/dhcpsrv/parsers/host_reservations_list_parser.h53
-rw-r--r--src/lib/dhcpsrv/parsers/ifaces_config_parser.cc130
-rw-r--r--src/lib/dhcpsrv/parsers/ifaces_config_parser.h70
-rw-r--r--src/lib/dhcpsrv/parsers/multi_threading_config_parser.cc73
-rw-r--r--src/lib/dhcpsrv/parsers/multi_threading_config_parser.h34
-rw-r--r--src/lib/dhcpsrv/parsers/option_data_parser.cc479
-rw-r--r--src/lib/dhcpsrv/parsers/option_data_parser.h240
-rw-r--r--src/lib/dhcpsrv/parsers/sanity_checks_parser.cc80
-rw-r--r--src/lib/dhcpsrv/parsers/sanity_checks_parser.h32
-rw-r--r--src/lib/dhcpsrv/parsers/shared_network_parser.cc429
-rw-r--r--src/lib/dhcpsrv/parsers/shared_network_parser.h126
-rw-r--r--src/lib/dhcpsrv/parsers/shared_networks_list_parser.h95
-rw-r--r--src/lib/dhcpsrv/parsers/simple_parser4.cc548
-rw-r--r--src/lib/dhcpsrv/parsers/simple_parser4.h69
-rw-r--r--src/lib/dhcpsrv/parsers/simple_parser6.cc564
-rw-r--r--src/lib/dhcpsrv/parsers/simple_parser6.h70
30 files changed, 7938 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/parsers/base_network_parser.cc b/src/lib/dhcpsrv/parsers/base_network_parser.cc
new file mode 100644
index 0000000..d5a21b2
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/base_network_parser.cc
@@ -0,0 +1,324 @@
+// Copyright (C) 2019-2023 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 <util/triplet.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/parsers/base_network_parser.h>
+#include <util/optional.h>
+#include <util/strutil.h>
+
+using namespace isc::data;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+void
+BaseNetworkParser::moveReservationMode(ElementPtr config) {
+ if (!config->contains("reservation-mode")) {
+ return;
+ }
+ if (config->contains("reservations-global") ||
+ config->contains("reservations-in-subnet") ||
+ config->contains("reservations-out-of-pool")) {
+ isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
+ " and one of 'reservations-global', 'reservations-in-subnet'"
+ " or 'reservations-out-of-pool' parameters");
+ }
+ std::string hr_mode = getString(config, "reservation-mode");
+ if ((hr_mode == "disabled") || (hr_mode == "off")) {
+ config->set("reservations-global", Element::create(false));
+ config->set("reservations-in-subnet", Element::create(false));
+ } else if (hr_mode == "out-of-pool") {
+ config->set("reservations-global", Element::create(false));
+ config->set("reservations-in-subnet", Element::create(true));
+ config->set("reservations-out-of-pool", Element::create(true));
+ } else if (hr_mode == "global") {
+ config->set("reservations-global", Element::create(true));
+ config->set("reservations-in-subnet", Element::create(false));
+ } else if (hr_mode == "all") {
+ config->set("reservations-global", Element::create(false));
+ config->set("reservations-in-subnet", Element::create(true));
+ config->set("reservations-out-of-pool", Element::create(false));
+ } else {
+ isc_throw(DhcpConfigError, "invalid reservation-mode parameter: '"
+ << hr_mode << "' ("
+ << getPosition("reservation-mode", config) << ")");
+ }
+ config->remove("reservation-mode");
+}
+
+void
+BaseNetworkParser::moveReservationMode(CfgGlobalsPtr config) {
+ if (!config->get(CfgGlobals::RESERVATION_MODE)) {
+ return;
+ }
+ if (config->get(CfgGlobals::RESERVATIONS_GLOBAL) ||
+ config->get(CfgGlobals::RESERVATIONS_IN_SUBNET) ||
+ config->get(CfgGlobals::RESERVATIONS_OUT_OF_POOL)) {
+ isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
+ " and one of 'reservations-global', 'reservations-in-subnet'"
+ " or 'reservations-out-of-pool' parameters");
+ }
+ std::string hr_mode = config->get(CfgGlobals::RESERVATION_MODE)->stringValue();
+ if ((hr_mode == "disabled") || (hr_mode == "off")) {
+ config->set(CfgGlobals::RESERVATIONS_GLOBAL, Element::create(false));
+ config->set(CfgGlobals::RESERVATIONS_IN_SUBNET, Element::create(false));
+ } else if (hr_mode == "out-of-pool") {
+ config->set(CfgGlobals::RESERVATIONS_GLOBAL, Element::create(false));
+ config->set(CfgGlobals::RESERVATIONS_IN_SUBNET, Element::create(true));
+ config->set(CfgGlobals::RESERVATIONS_OUT_OF_POOL, Element::create(true));
+ } else if (hr_mode == "global") {
+ config->set(CfgGlobals::RESERVATIONS_GLOBAL, Element::create(true));
+ config->set(CfgGlobals::RESERVATIONS_IN_SUBNET, Element::create(false));
+ } else if (hr_mode == "all") {
+ config->set(CfgGlobals::RESERVATIONS_GLOBAL, Element::create(false));
+ config->set(CfgGlobals::RESERVATIONS_IN_SUBNET, Element::create(true));
+ config->set("reservations-out-of-pool", Element::create(false));
+ } else {
+ isc_throw(DhcpConfigError, "invalid reservation-mode parameter: '"
+ << hr_mode << "' ("
+ << config->get(CfgGlobals::RESERVATION_MODE)->getPosition()
+ << ")");
+ }
+ config->set(CfgGlobals::RESERVATION_MODE, ConstElementPtr());
+}
+
+void
+BaseNetworkParser::parseCommon(const ConstElementPtr& network_data,
+ NetworkPtr& network) {
+ bool has_renew = network_data->contains("renew-timer");
+ bool has_rebind = network_data->contains("rebind-timer");
+ int64_t renew = -1;
+ int64_t rebind = -1;
+
+ if (has_renew) {
+ renew = getInteger(network_data, "renew-timer");
+ if (renew < 0) {
+ isc_throw(DhcpConfigError, "the value of renew-timer ("
+ << renew << ") must be a positive number");
+ }
+ network->setT1(renew);
+ }
+
+ if (has_rebind) {
+ rebind = getInteger(network_data, "rebind-timer");
+ if (rebind < 0) {
+ isc_throw(DhcpConfigError, "the value of rebind-timer ("
+ << rebind << ") must be a positive number");
+ }
+ network->setT2(rebind);
+ }
+
+ if (has_renew && has_rebind && (renew > rebind)) {
+ // The renew-timer value is too large and server logic
+ // later on will end up not sending it. Warn the user but
+ // allow the configuration to pass.
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_RENEW_GTR_REBIND)
+ .arg(network->getLabel())
+ .arg(renew)
+ .arg(rebind);
+ }
+
+ network->setValid(parseIntTriplet(network_data, "valid-lifetime"));
+
+ if (network_data->contains("store-extended-info")) {
+ network->setStoreExtendedInfo(getBoolean(network_data,
+ "store-extended-info"));
+ }
+
+ if (network_data->contains("reservations-global")) {
+ network->setReservationsGlobal(getBoolean(network_data,
+ "reservations-global"));
+ }
+
+ if (network_data->contains("reservations-in-subnet")) {
+ network->setReservationsInSubnet(getBoolean(network_data,
+ "reservations-in-subnet"));
+ }
+
+ if (network_data->contains("reservations-out-of-pool")) {
+ network->setReservationsOutOfPool(getBoolean(network_data,
+ "reservations-out-of-pool"));
+ }
+}
+
+void
+BaseNetworkParser::parseTeePercents(const ConstElementPtr& network_data,
+ NetworkPtr& network) {
+ bool calculate_tee_times = network->getCalculateTeeTimes();
+ if (network_data->contains("calculate-tee-times")) {
+ calculate_tee_times = getBoolean(network_data, "calculate-tee-times");
+ network->setCalculateTeeTimes(calculate_tee_times);
+ }
+
+ Optional<double> t2_percent;
+ if (network_data->contains("t2-percent")) {
+ t2_percent = getDouble(network_data, "t2-percent");
+ }
+
+ Optional<double> t1_percent;
+ if (network_data->contains("t1-percent")) {
+ t1_percent = getDouble(network_data, "t1-percent");
+ }
+ if (calculate_tee_times) {
+ 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.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.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());
+ }
+ }
+
+ network->setT2Percent(t2_percent);
+ network->setT1Percent(t1_percent);
+}
+
+void
+BaseNetworkParser::parseCacheParams(const ConstElementPtr& network_data,
+ NetworkPtr& network) {
+ if (network_data->contains("cache-threshold")) {
+ double cache_threshold = getDouble(network_data, "cache-threshold");
+ if ((cache_threshold <= 0.0) || (cache_threshold >= 1.0)) {
+ isc_throw(DhcpConfigError, "cache-threshold: " << cache_threshold
+ << " is invalid, it must be greater than 0.0 and less than 1.0");
+ }
+ network->setCacheThreshold(cache_threshold);
+ }
+
+ if (network_data->contains("cache-max-age")) {
+ network->setCacheMaxAge(getInteger(network_data, "cache-max-age"));
+ }
+}
+
+void
+BaseNetworkParser::parseDdnsParams(const data::ConstElementPtr& network_data,
+ NetworkPtr& network) {
+
+ if (network_data->contains("ddns-send-updates")) {
+ network->setDdnsSendUpdates(getBoolean(network_data, "ddns-send-updates"));
+ }
+
+ if (network_data->contains("ddns-override-no-update")) {
+ network->setDdnsOverrideNoUpdate(getBoolean(network_data, "ddns-override-no-update"));
+ }
+
+ if (network_data->contains("ddns-override-client-update")) {
+ network->setDdnsOverrideClientUpdate(getBoolean(network_data, "ddns-override-client-update"));
+ }
+
+ if (network_data->contains("ddns-replace-client-name")) {
+ network->setDdnsReplaceClientNameMode(getAndConvert<D2ClientConfig::ReplaceClientNameMode,
+ D2ClientConfig::stringToReplaceClientNameMode>
+ (network_data, "ddns-replace-client-name",
+ "ReplaceClientName mode"));
+ }
+
+ if (network_data->contains("ddns-generated-prefix")) {
+ network->setDdnsGeneratedPrefix(getString(network_data, "ddns-generated-prefix"));
+ }
+
+ if (network_data->contains("ddns-qualifying-suffix")) {
+ network->setDdnsQualifyingSuffix(getString(network_data, "ddns-qualifying-suffix"));
+ }
+
+ std::string hostname_char_set;
+ if (network_data->contains("hostname-char-set")) {
+ hostname_char_set = getString(network_data, "hostname-char-set");
+ network->setHostnameCharSet(hostname_char_set);
+ }
+
+ std::string hostname_char_replacement;
+ if (network_data->contains("hostname-char-replacement")) {
+ hostname_char_replacement = getString(network_data, "hostname-char-replacement");
+ network->setHostnameCharReplacement(hostname_char_replacement);
+ }
+
+ // We need to validate sanitizer values here so we can detect problems and
+ // cause a configuration. We don't retain the compilation because it's not
+ // something we can inherit.
+ if (!hostname_char_set.empty()) {
+ try {
+ str::StringSanitizerPtr sanitizer(new str::StringSanitizer(hostname_char_set,
+ hostname_char_replacement));
+ } catch (const std::exception& ex) {
+ isc_throw(BadValue, "hostname-char-set '" << hostname_char_set
+ << "' is not a valid regular expression");
+ }
+ }
+
+ if (network_data->contains("ddns-update-on-renew")) {
+ network->setDdnsUpdateOnRenew(getBoolean(network_data, "ddns-update-on-renew"));
+ }
+
+ if (network_data->contains("ddns-use-conflict-resolution")) {
+ network->setDdnsUseConflictResolution(getBoolean(network_data, "ddns-use-conflict-resolution"));
+ }
+
+ if (network_data->contains("ddns-ttl-percent")) {
+ network->setDdnsTtlPercent(getDouble(network_data, "ddns-ttl-percent"));
+ }
+}
+
+void
+BaseNetworkParser::parseAllocatorParams(const data::ConstElementPtr& network_data,
+ NetworkPtr& network) {
+ if (network_data->contains("allocator")) {
+ auto allocator_type = getString(network_data, "allocator");
+ if ((allocator_type != "iterative") && (allocator_type != "random") &&
+ (allocator_type != "flq")) {
+ // Unsupported allocator type used.
+ isc_throw(DhcpConfigError, "supported allocators are: iterative, random and flq");
+ }
+ network->setAllocatorType(allocator_type);
+ }
+}
+
+void
+BaseNetworkParser::parsePdAllocatorParams(const data::ConstElementPtr& network_data,
+ Network6Ptr& network) {
+ if (network_data->contains("pd-allocator")) {
+ auto allocator_type = getString(network_data, "pd-allocator");
+ if ((allocator_type != "iterative") && (allocator_type != "random") &&
+ (allocator_type != "flq")) {
+ // Unsupported allocator type used.
+ isc_throw(DhcpConfigError, "supported allocators are: iterative, random and flq");
+ }
+ network->setPdAllocatorType(allocator_type);
+ }
+}
+
+void
+BaseNetworkParser::parseOfferLft(const data::ConstElementPtr& network_data,
+ Network4Ptr& network) {
+ if (network_data->contains("offer-lifetime")) {
+ auto value = getInteger(network_data, "offer-lifetime");
+ if (value < 0) {
+ isc_throw(DhcpConfigError, "the value of offer-lifetime '"
+ << value << "' must be a positive number ("
+ << getPosition("offer-lifetime", network_data) << ")");
+ }
+
+ network->setOfferLft(value);
+ }
+}
+
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/base_network_parser.h b/src/lib/dhcpsrv/parsers/base_network_parser.h
new file mode 100644
index 0000000..19a1e1b
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/base_network_parser.h
@@ -0,0 +1,151 @@
+// Copyright (C) 2019-2023 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 BASE_NETWORK_PARSER_H
+#define BASE_NETWORK_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/cfg_globals.h>
+#include <dhcpsrv/network.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Common configuration parser for shared networks
+/// and subnets.
+class BaseNetworkParser : public data::SimpleParser {
+public:
+
+ /// @brief Moves deprecated reservation-mode parameter to
+ /// new reservations flags.
+ ///
+ /// @param config [in/out] configuration to alter.
+ /// @throw DhcpConfigError on error e.g. when both reservation-mode
+ /// and a flag are specified.
+ static void moveReservationMode(isc::data::ElementPtr config);
+
+ /// @brief Moves deprecated reservation-mode parameter to
+ /// new reservations flags.
+ ///
+ /// @param config [in/out] global parameters to alter.
+ /// @throw DhcpConfigError on error e.g. when both reservation-mode
+ /// and a flag are specified.
+ static void moveReservationMode(CfgGlobalsPtr config);
+
+protected:
+
+ /// @brief Parses common parameters
+ ///
+ /// The parsed parameters are:
+ /// - renew-timer,
+ /// - rebind-timer,
+ /// - valid-lifetime,
+ /// - store-extended-info
+ /// - reservations-global
+ /// - reservations-in-subnet
+ /// - reservations-out-of-pool
+ ///
+ /// @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 parseCommon(const data::ConstElementPtr& network_data,
+ NetworkPtr& network);
+
+ /// @brief Parses parameters related to "percent" timers settings.
+ ///
+ /// The parsed parameters are:
+ /// - calculate-tee-times,
+ /// - t1-percent,
+ /// - t2-percent.
+ ///
+ /// @param network_data Data element holding network configuration
+ /// to be parsed.
+ /// @param [out] network Pointer to a network in which parsed data is
+ /// to be stored.
+ ///
+ /// @throw DhcpConfigError if configuration of these parameters is
+ /// invalid.
+ void parseTeePercents(const data::ConstElementPtr& network_data,
+ NetworkPtr& network);
+
+ /// @brief Parses parameters related to lease cache settings.
+ ///
+ /// The parsed parameters are:
+ /// - cache-threshold,
+ /// - cache-max-age.
+ ///
+ /// @param network_data Data element holding network configuration
+ /// to be parsed.
+ /// @param [out] network Pointer to a network in which parsed data is
+ /// to be stored.
+ ///
+ /// @throw DhcpConfigError if configuration of these parameters is
+ /// invalid.
+ void parseCacheParams(const data::ConstElementPtr& network_data,
+ NetworkPtr& network);
+
+ /// @brief Parses parameters pertaining to DDNS behavior.
+ ///
+ /// The parsed parameters are:
+ /// - ddns-send-updates
+ /// - ddns-override-no-update
+ /// - ddns-override-client-update
+ /// - ddns-replace-client-name
+ /// - ddns-generated-prefix
+ /// - ddns-qualifying-suffix
+ /// - ddns-use-conflict-resolution
+ /// - ddns-update-on-renew
+ /// - ddns-ttl-percent
+ ///
+ /// @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 parseDdnsParams(const data::ConstElementPtr& network_data,
+ NetworkPtr& network);
+
+ /// @brief Parses parameters pertaining to allocator selection.
+ ///
+ /// The parsed parameters are:
+ /// - allocator
+ ///
+ /// @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 parseAllocatorParams(const data::ConstElementPtr& network_data,
+ NetworkPtr& network);
+
+ /// @brief Parses parameters pertaining to prefix delegation allocator
+ /// selection.
+ ///
+ /// The parsed parameters are:
+ /// - pd-allocator
+ ///
+ /// @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 parsePdAllocatorParams(const data::ConstElementPtr& network_data,
+ Network6Ptr& network);
+
+ /// @brief Parses offer-lifetime parameter (v4 only)
+ ///
+ /// @param network_data Data element holding shared network
+ /// configuration to be parsed.
+ /// @param [out] network Pointer to the v4 network in which parsed data is
+ /// to be stored.
+ /// @throw DhcpConfigError if the value is less than 0.
+ void parseOfferLft(const data::ConstElementPtr& network_data,
+ Network4Ptr& network);
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif
diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc
new file mode 100644
index 0000000..291632b
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc
@@ -0,0 +1,358 @@
+// Copyright (C) 2015-2023 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 <dhcp/libdhcp++.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/client_class_def.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/client_class_def_parser.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
+#include <eval/eval_context.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+
+#include <boost/foreach.hpp>
+#include <algorithm>
+#include <sstream>
+
+using namespace isc::data;
+using namespace isc::asiolink;
+using namespace isc::util;
+using namespace std;
+
+/// @file client_class_def_parser.cc
+///
+/// @brief Method implementations for client class definition parsing
+
+namespace isc {
+namespace dhcp {
+
+// ********************** ExpressionParser ****************************
+
+void
+ExpressionParser::parse(ExpressionPtr& expression,
+ ConstElementPtr expression_cfg,
+ uint16_t family,
+ EvalContext::CheckDefined check_defined,
+ EvalContext::ParserType parser_type) {
+ if (expression_cfg->getType() != Element::string) {
+ isc_throw(DhcpConfigError, "expression ["
+ << expression_cfg->str() << "] must be a string, at ("
+ << expression_cfg->getPosition() << ")");
+ }
+
+ // Get the expression's text via getValue() as the text returned
+ // by str() enclosed in quotes.
+ std::string value;
+ expression_cfg->getValue(value);
+
+ if (parser_type == EvalContext::PARSER_STRING && value.empty()) {
+ isc_throw(DhcpConfigError, "expression can not be empty at ("
+ << expression_cfg->getPosition() << ")");
+ }
+
+ try {
+ EvalContext eval_ctx(family == AF_INET ? Option::V4 : Option::V6,
+ check_defined);
+ eval_ctx.parseString(value, parser_type);
+ expression.reset(new Expression());
+ *expression = eval_ctx.expression;
+ } catch (const std::exception& ex) {
+ // Append position if there is a failure.
+ isc_throw(DhcpConfigError,
+ "expression: [" << value
+ << "] error: " << ex.what() << " at ("
+ << expression_cfg->getPosition() << ")");
+ }
+}
+
+// ********************** ClientClassDefParser ****************************
+
+void
+ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
+ ConstElementPtr class_def_cfg,
+ uint16_t family,
+ bool append_error_position,
+ bool check_dependencies) {
+ // name is now mandatory, so let's deal with it first.
+ std::string name = getString(class_def_cfg, "name");
+ if (name.empty()) {
+ isc_throw(DhcpConfigError,
+ "not empty parameter 'name' is required "
+ << getPosition("name", class_def_cfg) << ")");
+ }
+
+ EvalContext::ParserType parser_type = EvalContext::PARSER_BOOL;
+
+ // Let's try to parse the template-test expression
+ bool is_template = false;
+
+ // Parse matching expression
+ ExpressionPtr match_expr;
+ ConstElementPtr test_cfg = class_def_cfg->get("test");
+ ConstElementPtr template_test_cfg = class_def_cfg->get("template-test");
+ if (test_cfg && template_test_cfg) {
+ isc_throw(DhcpConfigError, "can not use both 'test' and 'template-test' ("
+ << test_cfg->getPosition() << ") and ("
+ << template_test_cfg->getPosition() << ")");
+ }
+ std::string test;
+ bool depend_on_known = false;
+ EvalContext::CheckDefined check_defined = EvalContext::acceptAll;
+ if (template_test_cfg) {
+ test_cfg = template_test_cfg;
+ parser_type = EvalContext::PARSER_STRING;
+ is_template = true;
+ } else {
+ check_defined = [&class_dictionary, &depend_on_known, check_dependencies](const ClientClass& cclass) {
+ return (!check_dependencies || isClientClassDefined(class_dictionary, depend_on_known, cclass));
+ };
+ }
+
+ if (test_cfg) {
+ ExpressionParser parser;
+ parser.parse(match_expr, test_cfg, family, check_defined, parser_type);
+ test = test_cfg->stringValue();
+ }
+
+ // Parse option def
+ CfgOptionDefPtr defs(new CfgOptionDef());
+ ConstElementPtr option_defs = class_def_cfg->get("option-def");
+ if (option_defs) {
+ // Apply defaults
+ SimpleParser::setListDefaults(option_defs,
+ family == AF_INET ?
+ SimpleParser4::OPTION4_DEF_DEFAULTS :
+ SimpleParser6::OPTION6_DEF_DEFAULTS);
+
+ OptionDefParser parser(family);
+ BOOST_FOREACH(ConstElementPtr option_def, option_defs->listValue()) {
+ OptionDefinitionPtr def = parser.parse(option_def);
+
+ // Verify if the definition is for an option which is in a deferred
+ // processing list.
+ if (!LibDHCP::shouldDeferOptionUnpack(def->getOptionSpaceName(),
+ def->getCode())) {
+ isc_throw(DhcpConfigError,
+ "Not allowed option definition for code '"
+ << def->getCode() << "' in space '"
+ << def->getOptionSpaceName() << "' at ("
+ << option_def->getPosition() << ")");
+ }
+ try {
+ defs->add(def);
+ } catch (const std::exception& ex) {
+ // Sanity check: it should never happen
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << option_def->getPosition() << ")");
+ }
+ }
+ }
+
+ // Parse option data
+ CfgOptionPtr options(new CfgOption());
+ ConstElementPtr option_data = class_def_cfg->get("option-data");
+ if (option_data) {
+ auto opts_parser = createOptionDataListParser(family, defs);
+ opts_parser->parse(options, option_data);
+ }
+
+ // Parse user context
+ ConstElementPtr user_context = class_def_cfg->get("user-context");
+ if (user_context) {
+ if (user_context->getType() != Element::map) {
+ isc_throw(isc::dhcp::DhcpConfigError, "User context has to be a map ("
+ << user_context->getPosition() << ")");
+ }
+ }
+
+ // Let's try to parse the only-if-required flag
+ bool required = false;
+ if (class_def_cfg->contains("only-if-required")) {
+ required = getBoolean(class_def_cfg, "only-if-required");
+ }
+
+ // Let's try to parse the next-server field
+ IOAddress next_server("0.0.0.0");
+ if (class_def_cfg->contains("next-server")) {
+ std::string next_server_txt = getString(class_def_cfg, "next-server");
+ try {
+ next_server = IOAddress(next_server_txt);
+ } catch (const IOError& ex) {
+ isc_throw(DhcpConfigError,
+ "Invalid next-server value specified: '"
+ << next_server_txt << "' ("
+ << getPosition("next-server", class_def_cfg) << ")");
+ }
+
+ if (next_server.getFamily() != AF_INET) {
+ isc_throw(DhcpConfigError, "Invalid next-server value: '"
+ << next_server_txt << "', must be IPv4 address ("
+ << getPosition("next-server", class_def_cfg) << ")");
+ }
+
+ if (next_server.isV4Bcast()) {
+ isc_throw(DhcpConfigError, "Invalid next-server value: '"
+ << next_server_txt << "', must not be a broadcast ("
+ << getPosition("next-server", class_def_cfg) << ")");
+ }
+ }
+
+ // Let's try to parse server-hostname
+ std::string sname;
+ if (class_def_cfg->contains("server-hostname")) {
+ sname = getString(class_def_cfg, "server-hostname");
+
+ if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
+ isc_throw(DhcpConfigError, "server-hostname must be at most "
+ << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
+ << sname.length() << " ("
+ << getPosition("server-hostname", class_def_cfg) << ")");
+ }
+ }
+
+ // Let's try to parse boot-file-name
+ std::string filename;
+ if (class_def_cfg->contains("boot-file-name")) {
+ filename = getString(class_def_cfg, "boot-file-name");
+
+ if (filename.length() > Pkt4::MAX_FILE_LEN) {
+ isc_throw(DhcpConfigError, "boot-file-name must be at most "
+ << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
+ << filename.length() << " ("
+ << getPosition("boot-file-name", class_def_cfg) << ")");
+ }
+ }
+
+ Optional<uint32_t> offer_lft;
+ if (class_def_cfg->contains("offer-lifetime")) {
+ auto value = getInteger(class_def_cfg, "offer-lifetime");
+ if (value < 0) {
+ isc_throw(DhcpConfigError, "the value of offer-lifetime '"
+ << value << "' must be a positive number ("
+ << getPosition("offer-lifetime", class_def_cfg) << ")");
+ }
+
+ offer_lft = value;
+ }
+
+ // Parse valid lifetime triplet.
+ Triplet<uint32_t> valid_lft = parseIntTriplet(class_def_cfg, "valid-lifetime");
+
+ Triplet<uint32_t> preferred_lft;
+ if (family != AF_INET) {
+ // Parse preferred lifetime triplet.
+ preferred_lft = parseIntTriplet(class_def_cfg, "preferred-lifetime");
+ }
+
+ // Sanity checks on built-in classes
+ for (auto bn : builtinNames) {
+ if (name == bn) {
+ if (required) {
+ isc_throw(DhcpConfigError, "built-in class '" << name
+ << "' only-if-required flag must be false");
+ }
+ if (!test.empty()) {
+ isc_throw(DhcpConfigError, "built-in class '" << name
+ << "' test expression must be empty");
+ }
+ }
+ }
+
+ // Sanity checks on DROP
+ if (name == "DROP") {
+ if (required) {
+ isc_throw(DhcpConfigError, "special class '" << name
+ << "' only-if-required flag must be false");
+ }
+ // depend_on_known is now allowed
+ }
+
+ // Add the client class definition
+ try {
+ class_dictionary->addClass(name, match_expr, test, required,
+ depend_on_known, options, defs,
+ user_context, next_server, sname, filename,
+ valid_lft, preferred_lft, is_template, offer_lft);
+ } catch (const std::exception& ex) {
+ std::ostringstream s;
+ s << "Can't add class: " << ex.what();
+ // Append position of the error in JSON string if required.
+ if (append_error_position) {
+ s << " (" << class_def_cfg->getPosition() << ")";
+ }
+ isc_throw(DhcpConfigError, s.str());
+ }
+}
+
+void
+ClientClassDefParser::checkParametersSupported(const ConstElementPtr& class_def_cfg,
+ const uint16_t family) {
+ // Make sure that the client class definition is stored in a map.
+ if (!class_def_cfg || (class_def_cfg->getType() != Element::map)) {
+ isc_throw(DhcpConfigError, "client class definition is not a map");
+ }
+
+ // Common v4 and v6 parameters supported for the client class.
+ static std::set<std::string> supported_params = { "name",
+ "test",
+ "option-data",
+ "user-context",
+ "only-if-required",
+ "valid-lifetime",
+ "min-valid-lifetime",
+ "max-valid-lifetime",
+ "template-test"};
+
+ // The v4 client class supports additional parameters.
+ static std::set<std::string> supported_params_v4 = { "option-def",
+ "next-server",
+ "server-hostname",
+ "boot-file-name" };
+
+ // The v6 client class supports additional parameters.
+ static std::set<std::string> supported_params_v6 = { "preferred-lifetime",
+ "min-preferred-lifetime",
+ "max-preferred-lifetime" };
+
+ // Iterate over the specified parameters and check if they are all supported.
+ for (auto name_value_pair : class_def_cfg->mapValue()) {
+ if ((supported_params.count(name_value_pair.first) > 0) ||
+ ((family == AF_INET) && (supported_params_v4.count(name_value_pair.first) > 0)) ||
+ ((family != AF_INET) && (supported_params_v6.count(name_value_pair.first) > 0))) {
+ continue;
+ } else {
+ isc_throw(DhcpConfigError, "unsupported client class parameter '"
+ << name_value_pair.first << "'");
+ }
+ }
+}
+
+boost::shared_ptr<OptionDataListParser>
+ClientClassDefParser::createOptionDataListParser(const uint16_t address_family,
+ CfgOptionDefPtr cfg_option_def) const {
+ auto parser = boost::make_shared<OptionDataListParser>(address_family, cfg_option_def);
+ return (parser);
+}
+
+// ****************** ClientClassDefListParser ************************
+
+ClientClassDictionaryPtr
+ClientClassDefListParser::parse(ConstElementPtr client_class_def_list,
+ uint16_t family, bool check_dependencies) {
+ ClientClassDictionaryPtr dictionary(new ClientClassDictionary());
+ BOOST_FOREACH(ConstElementPtr client_class_def,
+ client_class_def_list->listValue()) {
+ ClientClassDefParser parser;
+ parser.parse(dictionary, client_class_def, family, true, check_dependencies);
+ }
+ return (dictionary);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.h b/src/lib/dhcpsrv/parsers/client_class_def_parser.h
new file mode 100644
index 0000000..96e73e7
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/client_class_def_parser.h
@@ -0,0 +1,175 @@
+// Copyright (C) 2015-2022 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 CLIENT_CLASS_DEF_PARSER_H
+#define CLIENT_CLASS_DEF_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <eval/eval_context.h>
+#include <dhcpsrv/client_class_def.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <functional>
+#include <list>
+
+/// @file client_class_def_parser.h
+///
+/// @brief Parsers for client class definitions
+///
+/// These parsers are used to parse lists of client class definitions
+/// into a ClientClassDictionary of ClientClassDef instances. Each
+/// ClientClassDef consists of (at least) a name, an expression, option-def
+/// and option-data. Currently only a not empty name is required.
+///
+/// There parsers defined are:
+///
+/// ClientClassDefListParser - creates a ClientClassDictionary from a list
+/// of element maps, where each map contains the entries that specify a
+/// single class. The names of the classes in the are expected to be
+/// unique. Attempting to define a duplicate class will result in an
+/// DhcpConfigError throw. At the end the dictionary is stored by the CfgMgr.
+///
+/// ClientClassDefParser - creates a ClientClassDefinition from an element
+/// map. The elements are as follows:
+///
+/// -# "name" - a string containing the name of the class
+///
+/// -# "test" - a string containing the logical expression used to determine
+/// membership in the class. This is passed into the eval parser.
+///
+/// -# "option-def" - a list which defines the options which processing
+/// is deferred. This element is optional and parsed using the @ref
+/// isc::dhcp::OptionDefParser. A check is done to verify definitions
+/// are only for deferred processing option (DHCPv4 43 and 224-254).
+///
+/// -# "option-data" - a list which defines the options that should be
+/// assigned to remembers of the class. This element is optional and parsed
+/// using the @ref isc::dhcp::OptionDataListParser.
+///
+/// ExpressionParser - creates an eval::Expression from a string element,
+/// using the Eval Parser.
+///
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for a logical expression
+///
+/// This parser creates an instance of an Expression from a string. The
+/// string is passed to the Eval Parser and the resultant Expression is
+/// stored into the ExpressionPtr reference passed into the constructor.
+class ExpressionParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Parses an expression configuration element into an Expression
+ ///
+ /// @param expression variable in which to store the new expression
+ /// @param expression_cfg the configuration entry to be parsed.
+ /// @param family the address family of the expression.
+ /// @param check_defined a closure to check if a client class is defined.
+ /// @param parser_type the expected type of the evaluated expression.
+ ///
+ /// @throw DhcpConfigError if parsing was unsuccessful.
+ void parse(ExpressionPtr& expression,
+ isc::data::ConstElementPtr expression_cfg,
+ uint16_t family,
+ isc::eval::EvalContext::CheckDefined check_defined = isc::eval::EvalContext::acceptAll,
+ isc::eval::EvalContext::ParserType parser_type = isc::eval::EvalContext::PARSER_BOOL);
+};
+
+/// @brief Parser for a single client class definition.
+///
+/// This parser creates an instance of a client class definition.
+class ClientClassDefParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Virtual destructor.
+ virtual ~ClientClassDefParser() {
+ }
+
+ /// @brief Parses an entry that describes single client class definition.
+ ///
+ /// Attempts to add the new class directly into the given dictionary.
+ /// This done here to detect duplicate classes prior to commit().
+ /// @param class_dictionary dictionary into which the class should be added
+ /// @param client_class_def a configuration entry to be parsed.
+ /// @param family the address family of the client class.
+ /// @param append_error_position Boolean flag indicating if position
+ /// of the parsed string within parsed JSON should be appended. The
+ /// default setting is to append it, but it is typically set to false
+ /// when this parser is used by hooks libraries.
+ /// @param check_dependencies indicates if the parser should evaluate an
+ /// expression to see if the referenced client classes exist.
+ ///
+ /// @throw DhcpConfigError if parsing was unsuccessful.
+ void parse(ClientClassDictionaryPtr& class_dictionary,
+ isc::data::ConstElementPtr client_class_def,
+ uint16_t family,
+ bool append_error_position = true,
+ bool check_dependencies = true);
+
+ /// @brief Iterates over class parameters and checks if they are supported.
+ ///
+ /// This method should be called by hooks libraries which do not use Bison
+ /// to validate class syntax prior to parsing the client class information.
+ ///
+ /// @param class_def_cfg class configuration entry.
+ /// @param family the address family of the client class.
+ ///
+ /// @throw DhcpConfigError if any of the parameters is not supported.
+ void checkParametersSupported(const isc::data::ConstElementPtr& class_def_cfg,
+ const uint16_t family);
+
+protected:
+
+ /// @brief Returns an instance of the @c OptionDataListParser to
+ /// be used in parsing the option-data structure.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for option data.
+ ///
+ /// @param address_family @c AF_INET (for DHCPv4) or @c AF_INET6 (for DHCPv6).
+ /// @param cfg_option_def structure holding option definitions.
+ ///
+ /// @return an instance of the @c OptionDataListParser.
+ virtual boost::shared_ptr<OptionDataListParser>
+ createOptionDataListParser(const uint16_t address_family,
+ CfgOptionDefPtr cfg_option_def) const;
+};
+
+/// @brief Defines a pointer to a ClientClassDefParser
+typedef boost::shared_ptr<ClientClassDefParser> ClientClassDefParserPtr;
+
+/// @brief Parser for a list of client class definitions.
+///
+/// This parser iterates over all configuration entries that define
+/// client classes and creates ClientClassDef instances for each.
+/// When the parsing successfully completes, the collection of
+/// created definitions is given to the CfgMgr.
+class ClientClassDefListParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Parse configuration entries.
+ ///
+ /// This function parses configuration entries, creates instances
+ /// of client class definitions and tries to adds them to the
+ /// local dictionary. At the end the dictionary is returned.
+ ///
+ /// @param class_def_list pointer to an element that holds entries
+ /// for client class definitions.
+ /// @param family the address family of the client class definitions.
+ /// @param check_dependencies indicates if the parser should evaluate an
+ /// expression to see if the referenced client classes exist.
+ /// @return a pointer to the filled dictionary
+ /// @throw DhcpConfigError if configuration parsing fails.
+ ClientClassDictionaryPtr
+ parse(isc::data::ConstElementPtr class_def_list, uint16_t family,
+ bool check_dependencies = true);
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // CLIENT_CLASS_DEF_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
new file mode 100644
index 0000000..384369c
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
@@ -0,0 +1,1676 @@
+// Copyright (C) 2013-2023 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 <dhcp/iface_mgr.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/host_reservation_parser.h>
+#include <dhcpsrv/parsers/host_reservations_list_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
+#include <dhcpsrv/cfg_mac_source.h>
+#include <util/encode/hex.h>
+#include <util/strutil.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <limits>
+#include <map>
+#include <string>
+#include <vector>
+#include <iomanip>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+// ******************** MACSourcesListConfigParser *************************
+
+void
+MACSourcesListConfigParser::parse(CfgMACSource& mac_sources, ConstElementPtr value) {
+ uint32_t source = 0;
+ size_t cnt = 0;
+
+ // By default, there's only one source defined: ANY.
+ // If user specified anything, we need to get rid of that default.
+ mac_sources.clear();
+
+ BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
+ std::string source_str = source_elem->stringValue();
+ try {
+ source = CfgMACSource::MACSourceFromText(source_str);
+ mac_sources.add(source);
+ ++cnt;
+ } catch (const InvalidParameter& ex) {
+ isc_throw(DhcpConfigError, "The mac-sources value '" << source_str
+ << "' was specified twice (" << value->getPosition() << ")");
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, "Failed to convert '"
+ << source_str << "' to any recognized MAC source:"
+ << ex.what() << " (" << value->getPosition() << ")");
+ }
+ }
+
+ if (!cnt) {
+ isc_throw(DhcpConfigError, "If specified, MAC Sources cannot be empty");
+ }
+}
+
+// ******************** ControlSocketParser *************************
+void ControlSocketParser::parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value) {
+ if (!value) {
+ // Sanity check: not supposed to fail.
+ isc_throw(DhcpConfigError, "Logic error: specified control-socket is null");
+ }
+
+ if (value->getType() != Element::map) {
+ // Sanity check: not supposed to fail.
+ isc_throw(DhcpConfigError, "Specified control-socket is expected to be a map"
+ ", i.e. a structure defined within { }");
+ }
+ srv_cfg.setControlSocketInfo(value);
+}
+
+// ******************************** OptionDefParser ****************************
+
+OptionDefParser::OptionDefParser(const uint16_t address_family)
+ : address_family_(address_family) {
+}
+
+OptionDefinitionPtr
+OptionDefParser::parse(ConstElementPtr option_def) {
+
+ // Check parameters.
+ if (address_family_ == AF_INET) {
+ checkKeywords(SimpleParser4::OPTION4_DEF_PARAMETERS, option_def);
+ } else {
+ checkKeywords(SimpleParser6::OPTION6_DEF_PARAMETERS, option_def);
+ }
+
+ // Get mandatory parameters.
+ std::string name = getString(option_def, "name");
+ int64_t code64 = getInteger(option_def, "code");
+ std::string type = getString(option_def, "type");
+
+ // Get optional parameters. Whoever called this parser, should have
+ // called SimpleParser::setDefaults first.
+ bool array_type = getBoolean(option_def, "array");
+ std::string record_types = getString(option_def, "record-types");
+ std::string space = getString(option_def, "space");
+ std::string encapsulates = getString(option_def, "encapsulate");
+ ConstElementPtr user_context = option_def->get("user-context");
+
+ // Check code value.
+ if (code64 < 0) {
+ isc_throw(DhcpConfigError, "option code must not be negative "
+ "(" << getPosition("code", option_def) << ")");
+ } else if (address_family_ == AF_INET &&
+ code64 > std::numeric_limits<uint8_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << code64
+ << "', it must not be greater than '"
+ << static_cast<int>(std::numeric_limits<uint8_t>::max())
+ << "' (" << getPosition("code", option_def) << ")");
+ } else if (address_family_ == AF_INET6 &&
+ code64 > std::numeric_limits<uint16_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << code64
+ << "', it must not be greater than '"
+ << std::numeric_limits<uint16_t>::max()
+ << "' (" << getPosition("code", option_def) << ")");
+ }
+ uint32_t code = static_cast<uint32_t>(code64);
+
+ // Validate space name.
+ if (!OptionSpace::validateName(space)) {
+ isc_throw(DhcpConfigError, "invalid option space name '"
+ << space << "' ("
+ << getPosition("space", option_def) << ")");
+ }
+
+ // Protect against definition of options 0 (PAD) or 255 (END)
+ // in (and only in) the dhcp4 space.
+ if (space == DHCP4_OPTION_SPACE) {
+ if (code == DHO_PAD) {
+ isc_throw(DhcpConfigError, "invalid option code '0': "
+ << "reserved for PAD ("
+ << getPosition("code", option_def) << ")");
+ } else if (code == DHO_END) {
+ isc_throw(DhcpConfigError, "invalid option code '255': "
+ << "reserved for END ("
+ << getPosition("code", option_def) << ")");
+ }
+ }
+
+ // For dhcp6 space the value 0 is reserved.
+ if (space == DHCP6_OPTION_SPACE) {
+ if (code == 0) {
+ isc_throw(DhcpConfigError, "invalid option code '0': "
+ << "reserved value ("
+ << getPosition("code", option_def) << ")");
+ }
+ }
+
+ // Create option definition.
+ OptionDefinitionPtr def;
+ // We need to check if user has set encapsulated option space
+ // name. If so, different constructor will be used.
+ if (!encapsulates.empty()) {
+ // Arrays can't be used together with sub-options.
+ if (array_type) {
+ isc_throw(DhcpConfigError, "option '" << space << "."
+ << name << "', comprising an array of data"
+ << " fields may not encapsulate any option space ("
+ << option_def->getPosition() << ")");
+
+ } else if (encapsulates == space) {
+ isc_throw(DhcpConfigError, "option must not encapsulate"
+ << " an option space it belongs to: '"
+ << space << "." << name << "' is set to"
+ << " encapsulate '" << space << "' ("
+ << option_def->getPosition() << ")");
+
+ } else {
+ def.reset(new OptionDefinition(name, code, space, type,
+ encapsulates.c_str()));
+ }
+
+ } else {
+ def.reset(new OptionDefinition(name, code, space, type, array_type));
+
+ }
+
+ if (user_context) {
+ def->setContext(user_context);
+ }
+
+ // Split the list of record types into tokens.
+ std::vector<std::string> record_tokens =
+ isc::util::str::tokens(record_types, ",");
+ // Iterate over each token and add a record type into
+ // option definition.
+ BOOST_FOREACH(std::string record_type, record_tokens) {
+ try {
+ boost::trim(record_type);
+ if (!record_type.empty()) {
+ def->addRecordField(record_type);
+ }
+ } catch (const Exception& ex) {
+ isc_throw(DhcpConfigError, "invalid record type values"
+ << " specified for the option definition: "
+ << ex.what() << " ("
+ << getPosition("record-types", option_def) << ")");
+ }
+ }
+
+ // Validate the definition.
+ try {
+ def->validate();
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what()
+ << " (" << option_def->getPosition() << ")");
+ }
+
+ // Option definition has been created successfully.
+ return (def);
+}
+
+// ******************************** OptionDefListParser ************************
+
+OptionDefListParser::OptionDefListParser(const uint16_t address_family)
+ : address_family_(address_family) {
+}
+
+void
+OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_list) {
+ if (!option_def_list) {
+ // Sanity check: not supposed to fail.
+ isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
+ << " option definitions is NULL ("
+ << option_def_list->getPosition() << ")");
+ }
+
+ OptionDefParser parser(address_family_);
+ BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
+ OptionDefinitionPtr def = parser.parse(option_def);
+ try {
+ storage->add(def);
+ } catch (const std::exception& ex) {
+ // Append position if there is a failure.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << option_def->getPosition() << ")");
+ }
+ }
+
+ // All definitions have been prepared. Put them as runtime options into
+ // the libdhcp++.
+ LibDHCP::setRuntimeOptionDefs(storage->getContainer());
+}
+
+//****************************** RelayInfoParser ********************************
+RelayInfoParser::RelayInfoParser(const Option::Universe& family)
+ : family_(family) {
+};
+
+void
+RelayInfoParser::parse(const isc::dhcp::Network::RelayInfoPtr& relay_info,
+ ConstElementPtr relay_elem) {
+
+ if (relay_elem->getType() != Element::map) {
+ isc_throw(DhcpConfigError, "relay must be a map");
+ }
+
+ ConstElementPtr address = relay_elem->get("ip-address");
+ ConstElementPtr addresses = relay_elem->get("ip-addresses");
+
+ if (address && addresses) {
+ isc_throw(DhcpConfigError,
+ "specify either ip-address or ip-addresses, not both");
+ }
+
+ if (!address && !addresses) {
+ isc_throw(DhcpConfigError, "ip-addresses is required");
+ }
+
+ // Create our resultant RelayInfo structure
+ *relay_info = isc::dhcp::Network::RelayInfo();
+
+ if (address) {
+ addAddress("ip-address", getString(relay_elem, "ip-address"),
+ relay_elem, relay_info);
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CFGMGR_RELAY_IP_ADDRESS_DEPRECATED)
+ .arg(getPosition("ip-address", relay_elem));
+ return;
+ }
+
+ if (addresses->getType() != Element::list) {
+ isc_throw(DhcpConfigError, "ip-addresses must be a list "
+ "(" << getPosition("ip-addresses", relay_elem) << ")");
+ }
+
+ BOOST_FOREACH(ConstElementPtr address_element, addresses->listValue()) {
+ addAddress("ip-addresses", address_element->stringValue(),
+ relay_elem, relay_info);
+ }
+}
+
+void
+RelayInfoParser::addAddress(const std::string& name,
+ const std::string& address_str,
+ ConstElementPtr relay_elem,
+ const isc::dhcp::Network::RelayInfoPtr& relay_info) {
+ boost::scoped_ptr<isc::asiolink::IOAddress> ip;
+ try {
+ ip.reset(new isc::asiolink::IOAddress(address_str));
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, "address " << address_str
+ << " is not a valid: "
+ << (family_ == Option::V4 ? "IPv4" : "IPv6")
+ << "address"
+ << " (" << getPosition(name, relay_elem) << ")");
+ }
+
+ // Check if the address family matches.
+ if ((ip->isV4() && family_ != Option::V4) ||
+ (ip->isV6() && family_ != Option::V6) ) {
+ isc_throw(DhcpConfigError, "address " << address_str
+ << " is not a: "
+ << (family_ == Option::V4 ? "IPv4" : "IPv6")
+ << "address"
+ << " (" << getPosition(name, relay_elem) << ")");
+ }
+
+ try {
+ relay_info->addAddress(*ip);
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, "cannot add address: " << address_str
+ << " to relay info: " << ex.what()
+ << " (" << getPosition(name, relay_elem) << ")");
+ }
+}
+
+//****************************** PoolParser ********************************
+
+void
+PoolParser::parse(PoolStoragePtr pools,
+ ConstElementPtr pool_structure,
+ const uint16_t address_family,
+ bool encapsulate_options) {
+
+ if (address_family == AF_INET) {
+ checkKeywords(SimpleParser4::POOL4_PARAMETERS, pool_structure);
+ } else {
+ checkKeywords(SimpleParser6::POOL6_PARAMETERS, pool_structure);
+ }
+
+ ConstElementPtr text_pool = pool_structure->get("pool");
+
+ if (!text_pool) {
+ isc_throw(DhcpConfigError, "Mandatory 'pool' entry missing in "
+ "definition: (" << pool_structure->getPosition() << ")");
+ }
+
+ // That should be a single pool representation. It should contain
+ // text is form prefix/len or first - last. Note that spaces
+ // are allowed
+ string txt = text_pool->stringValue();
+
+ // first let's remove any whitespaces
+ boost::erase_all(txt, " "); // space
+ boost::erase_all(txt, "\t"); // tabulation
+
+ PoolPtr pool;
+
+ // Is this prefix/len notation?
+ size_t pos = txt.find("/");
+ if (pos != string::npos) {
+ isc::asiolink::IOAddress addr("::");
+ uint8_t len = 0;
+ try {
+ addr = isc::asiolink::IOAddress(txt.substr(0, pos));
+
+ // start with the first character after /
+ string prefix_len = txt.substr(pos + 1);
+
+ // It is lexical cast to int and then downcast to uint8_t.
+ // Direct cast to uint8_t (which is really an unsigned char)
+ // will result in interpreting the first digit as output
+ // value and throwing exception if length is written on two
+ // digits (because there are extra characters left over).
+
+ // No checks for values over 128. Range correctness will
+ // be checked in Pool4 constructor, here we only check
+ // the representation fits in an uint8_t as this can't
+ // be done by a direct lexical cast as explained...
+ int val_len = boost::lexical_cast<int>(prefix_len);
+ if ((val_len < std::numeric_limits<uint8_t>::min()) ||
+ (val_len > std::numeric_limits<uint8_t>::max())) {
+ // This exception will be handled 4 line later!
+ isc_throw(OutOfRange, "");
+ }
+ len = static_cast<uint8_t>(val_len);
+ } catch (...) {
+ isc_throw(DhcpConfigError, "Failed to parse pool "
+ "definition: " << txt << " ("
+ << text_pool->getPosition() << ")");
+ }
+
+ try {
+ pool = poolMaker(addr, len);
+ pools->push_back(pool);
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, "Failed to create pool defined by: "
+ << txt << " (" << text_pool->getPosition() << ")");
+ }
+
+ } else {
+ isc::asiolink::IOAddress min("::");
+ isc::asiolink::IOAddress max("::");
+
+ // Is this min-max notation?
+ pos = txt.find("-");
+ if (pos != string::npos) {
+ // using min-max notation
+ try {
+ min = isc::asiolink::IOAddress(txt.substr(0, pos));
+ max = isc::asiolink::IOAddress(txt.substr(pos + 1));
+ } catch (...) {
+ isc_throw(DhcpConfigError, "Failed to parse pool "
+ "definition: " << txt << " ("
+ << text_pool->getPosition() << ")");
+ }
+
+ try {
+ pool = poolMaker(min, max);
+ pools->push_back(pool);
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, "Failed to create pool defined by: "
+ << txt << " (" << text_pool->getPosition() << ")");
+ }
+ }
+ }
+
+ if (!pool) {
+ isc_throw(DhcpConfigError, "invalid pool definition: "
+ << text_pool->stringValue() <<
+ ". There are two acceptable formats <min address-max address>"
+ " or <prefix/len> ("
+ << text_pool->getPosition() << ")");
+ }
+
+ // If there is a pool-id, store it.
+ ConstElementPtr pool_id = pool_structure->get("pool-id");
+ if (pool_id) {
+ if (pool_id->intValue() <= 0) {
+ isc_throw(BadValue, "pool-id " << pool_id->intValue() << " is not"
+ << " a positive integer greater than 0");
+ } else if (pool_id->intValue() > numeric_limits<uint32_t>::max()) {
+ isc_throw(BadValue, "pool-id " << pool_id->intValue() << " is not"
+ << " a 32 bit unsigned integer");
+ }
+
+ pool->setID(pool_id->intValue());
+ }
+
+ // If there's user-context specified, store it.
+ ConstElementPtr user_context = pool_structure->get("user-context");
+ if (user_context) {
+ // The grammar accepts only maps but still check it.
+ if (user_context->getType() != Element::map) {
+ isc_throw(isc::dhcp::DhcpConfigError, "User context has to be a map ("
+ << user_context->getPosition() << ")");
+ }
+ pool->setContext(user_context);
+ }
+
+ // Parser pool specific options.
+ ConstElementPtr option_data = pool_structure->get("option-data");
+ if (option_data) {
+ try {
+ CfgOptionPtr cfg = pool->getCfgOption();
+ auto option_parser = createOptionDataListParser(address_family);
+ option_parser->parse(cfg, option_data, encapsulate_options);
+ } catch (const std::exception& ex) {
+ isc_throw(isc::dhcp::DhcpConfigError, ex.what()
+ << " (" << option_data->getPosition() << ")");
+ }
+ }
+
+ // Client-class.
+ ConstElementPtr client_class = pool_structure->get("client-class");
+ if (client_class) {
+ string cclass = client_class->stringValue();
+ if (!cclass.empty()) {
+ pool->allowClientClass(cclass);
+ }
+ }
+
+ // Try setting up required client classes.
+ ConstElementPtr class_list = pool_structure->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() << ")");
+ }
+ pool->requireClientClass((*cclass)->stringValue());
+ }
+ }
+}
+
+boost::shared_ptr<OptionDataListParser>
+PoolParser::createOptionDataListParser(const uint16_t address_family) const {
+ auto parser = boost::make_shared<OptionDataListParser>(address_family);
+ return (parser);
+}
+
+//****************************** Pool4Parser *************************
+
+PoolPtr
+Pool4Parser::poolMaker (IOAddress &addr, uint32_t len, int32_t) {
+ return (PoolPtr(new Pool4(addr, len)));
+}
+
+PoolPtr
+Pool4Parser::poolMaker (IOAddress &min, IOAddress &max, int32_t) {
+ return (PoolPtr(new Pool4(min, max)));
+}
+
+//****************************** Pools4ListParser *************************
+
+void
+Pools4ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list,
+ bool encapsulate_options) {
+ BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
+ auto parser = createPoolConfigParser();
+ parser->parse(pools, pool, AF_INET, encapsulate_options);
+ }
+}
+
+boost::shared_ptr<PoolParser>
+Pools4ListParser::createPoolConfigParser() const {
+ auto parser = boost::make_shared<Pool4Parser>();
+ return (parser);
+}
+
+//****************************** SubnetConfigParser *************************
+
+SubnetConfigParser::SubnetConfigParser(uint16_t family, bool check_iface)
+ : pools_(new PoolStorage()),
+ address_family_(family),
+ check_iface_(check_iface) {
+ relay_info_.reset(new isc::dhcp::Network::RelayInfo());
+}
+
+SubnetPtr
+SubnetConfigParser::parse(ConstElementPtr subnet, bool encapsulate_options) {
+
+ ConstElementPtr relay_params = subnet->get("relay");
+ if (relay_params) {
+ Option::Universe u = (address_family_ == AF_INET) ? Option::V4 : Option::V6;
+ RelayInfoParser parser(u);
+ parser.parse(relay_info_, relay_params);
+ }
+
+ // Create a subnet.
+ try {
+ createSubnet(subnet);
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError,
+ "subnet configuration failed: " << ex.what());
+ }
+
+ // We create subnet first and then parse the options straight into the subnet's
+ // CfgOption structure. Previously, we first parsed the options and then copied
+ // them into the CfgOption after creating the subnet but it had two issues. First,
+ // it cost performance. Second, copying options reset the isEncapsulated() flag.
+ // If the options have been encapsulated we want to preserve the flag to ensure
+ // they are not encapsulated several times.
+ ConstElementPtr options_params = subnet->get("option-data");
+ if (options_params) {
+ auto opt_parser = createOptionDataListParser();
+ opt_parser->parse(subnet_->getCfgOption(), options_params, encapsulate_options);
+ }
+
+ return (subnet_);
+}
+
+void
+SubnetConfigParser::createSubnet(ConstElementPtr params) {
+ std::string subnet_txt;
+ try {
+ subnet_txt = getString(params, "subnet");
+ } catch (const DhcpConfigError &) {
+ // rethrow with precise error
+ isc_throw(DhcpConfigError,
+ "mandatory 'subnet' parameter is missing for a subnet being"
+ " configured (" << params->getPosition() << ")");
+ }
+
+ // Remove any spaces or tabs.
+ boost::erase_all(subnet_txt, " ");
+ boost::erase_all(subnet_txt, "\t");
+
+ // The subnet format is prefix/len. We are going to extract
+ // the prefix portion of a subnet string to create IOAddress
+ // object from it. IOAddress will be passed to the Subnet's
+ // constructor later on. In order to extract the prefix we
+ // need to get all characters preceding "/".
+ size_t pos = subnet_txt.find("/");
+ if (pos == string::npos) {
+ ConstElementPtr elem = params->get("subnet");
+ isc_throw(DhcpConfigError,
+ "Invalid subnet syntax (prefix/len expected):" << subnet_txt
+ << " (" << elem->getPosition() << ")");
+ }
+
+ // Try to create the address object. It also validates that
+ // the address syntax is ok.
+ isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
+
+ // Now parse out the prefix length.
+ unsigned int len;
+ try {
+ len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+ } catch (const boost::bad_lexical_cast&) {
+ ConstElementPtr elem = params->get("subnet");
+ isc_throw(DhcpConfigError, "prefix length: '" <<
+ subnet_txt.substr(pos+1) << "' is not an integer ("
+ << elem->getPosition() << ")");
+ }
+
+ // Sanity check the prefix length
+ if ((addr.isV6() && len > 128) ||
+ (addr.isV4() && len > 32)) {
+ ConstElementPtr elem = params->get("subnet");
+ isc_throw(BadValue,
+ "Invalid prefix length specified for subnet: " << len
+ << " (" << elem->getPosition() << ")");
+ }
+
+ // Call the subclass's method to instantiate the subnet
+ initSubnet(params, addr, len);
+
+ // Add pools to it.
+ for (const auto& pool : *pools_) {
+ try {
+ subnet_->addPool(pool);
+ } catch (const BadValue& ex) {
+ // addPool() can throw BadValue if the pool is overlapping or
+ // is out of bounds for the subnet.
+ isc_throw(DhcpConfigError,
+ ex.what() << " (" << params->getPosition() << ")");
+ }
+ }
+ // If there's user-context specified, store it.
+ ConstElementPtr user_context = params->get("user-context");
+ if (user_context) {
+ // The grammar accepts only maps but still check it.
+ if (user_context->getType() != Element::map) {
+ isc_throw(isc::dhcp::DhcpConfigError, "User context has to be a map ("
+ << user_context->getPosition() << ")");
+ }
+ subnet_->setContext(user_context);
+ }
+
+ // In order to take advantage of the dynamic inheritance of global
+ // parameters to a subnet we need to set a callback function for each
+ // subnet to allow for fetching global parameters.
+ subnet_->setFetchGlobalsFn([]() -> ConstCfgGlobalsPtr {
+ return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+ });
+}
+
+boost::shared_ptr<OptionDataListParser>
+SubnetConfigParser::createOptionDataListParser() const {
+ auto parser = boost::make_shared<OptionDataListParser>(address_family_);
+ return (parser);
+}
+
+//****************************** Subnet4ConfigParser *************************
+
+Subnet4ConfigParser::Subnet4ConfigParser(bool check_iface)
+ : SubnetConfigParser(AF_INET, check_iface) {
+}
+
+Subnet4Ptr
+Subnet4ConfigParser::parse(ConstElementPtr subnet, bool encapsulate_options) {
+ // Check parameters.
+ checkKeywords(SimpleParser4::SUBNET4_PARAMETERS, subnet);
+
+ /// Parse Pools first.
+ ConstElementPtr pools = subnet->get("pools");
+ if (pools) {
+ auto parser = createPoolsListParser();
+ parser->parse(pools_, pools, encapsulate_options);
+ }
+
+ SubnetPtr generic = SubnetConfigParser::parse(subnet, encapsulate_options);
+
+ if (!generic) {
+ // Sanity check: not supposed to fail.
+ isc_throw(DhcpConfigError,
+ "Failed to create an IPv4 subnet (" <<
+ subnet->getPosition() << ")");
+ }
+
+ Subnet4Ptr sn4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
+ if (!sn4ptr) {
+ // If we hit this, it is a programming error.
+ isc_throw(Unexpected,
+ "Invalid Subnet4 cast in Subnet4ConfigParser::parse");
+ }
+
+ // Set relay information if it was parsed
+ if (relay_info_) {
+ sn4ptr->setRelayInfo(*relay_info_);
+ }
+
+ // Parse Host Reservations for this subnet if any.
+ ConstElementPtr reservations = subnet->get("reservations");
+ if (reservations) {
+ HostCollection hosts;
+ HostReservationsListParser<HostReservationParser4> parser;
+ parser.parse(subnet_->getID(), reservations, hosts);
+ for (auto h = hosts.begin(); h != hosts.end(); ++h) {
+ validateResv(sn4ptr, *h);
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
+ }
+ }
+
+ // Parse allocator specification.
+ auto network4 = boost::dynamic_pointer_cast<Network>(sn4ptr);
+ parseAllocatorParams(subnet, network4);
+
+ // Instantiate the allocator.
+ sn4ptr->createAllocators();
+
+ return (sn4ptr);
+}
+
+void
+Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
+ asiolink::IOAddress addr, uint8_t len) {
+ // 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.
+ int64_t subnet_id_max = static_cast<int64_t>(SUBNET_ID_MAX);
+ SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id", 0,
+ subnet_id_max));
+
+ auto subnet4 = Subnet4::create(addr, len, Triplet<uint32_t>(),
+ Triplet<uint32_t>(), Triplet<uint32_t>(),
+ subnet_id);
+ subnet_ = subnet4;
+
+ // Move from reservation mode to new reservations flags.
+ ElementPtr mutable_params;
+ mutable_params = boost::const_pointer_cast<Element>(params);
+ // @todo add warning
+ BaseNetworkParser::moveReservationMode(mutable_params);
+
+ // Parse parameters common to all Network derivations.
+ NetworkPtr network = boost::dynamic_pointer_cast<Network>(subnet4);
+ parseCommon(mutable_params, network);
+
+ std::ostringstream output;
+ output << addr << "/" << static_cast<int>(len) << " with params: ";
+
+ bool has_renew = !subnet4->getT1().unspecified();
+ bool has_rebind = !subnet4->getT2().unspecified();
+ int64_t renew = -1;
+ int64_t rebind = -1;
+
+ // t1 and t2 are optional may be not specified.
+ if (has_renew) {
+ renew = subnet4->getT1().get();
+ output << "t1=" << renew << ", ";
+ }
+ if (has_rebind) {
+ rebind = subnet4->getT2().get();
+ output << "t2=" << rebind << ", ";
+ }
+
+ if (!subnet4->getValid().unspecified()) {
+ output << "valid-lifetime=" << subnet4->getValid().get();
+ }
+
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_NEW_SUBNET4).arg(output.str());
+
+ // 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.
+ 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 "".
+ 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 << ")");
+ }
+ }
+
+ // Set server-hostname.
+ 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);
+ }
+ }
+
+ // Set boot-file-name.
+ 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);
+ }
+ }
+
+ // Get interface name. If it is defined, then the subnet is available
+ // directly over specified network interface.
+ if (params->contains("interface")) {
+ std::string iface = getString(params, "interface");
+ if (!iface.empty()) {
+ if (check_iface_ && !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);
+ }
+ }
+
+ // Try setting up 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.
+ 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() << ")");
+ }
+ subnet4->requireClientClass((*cclass)->stringValue());
+ }
+ }
+
+ // 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 (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");
+ }
+ subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
+ subnet4->get4o6().enabled(true);
+ }
+ }
+
+ // Try 4o6 specific parameter: 4o6-interface-id
+ 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
+ /// code (see isc::data::SubnetConfigParser::createSubnet)
+
+ // Here globally defined options were merged to the subnet specific
+ // options but this is no longer the case (they have a different
+ // and not consecutive priority).
+
+ // Parse t1-percent and t2-percent
+ parseTeePercents(params, network);
+
+ // Parse DDNS parameters
+ parseDdnsParams(params, network);
+
+ // Parse lease cache parameters
+ parseCacheParams(params, network);
+
+ // Set the offer_lft value for the subnet.
+ if (params->contains("offer-lifetime")) {
+ uint32_t offer_lft = getInteger(params, "offer-lifetime");
+ subnet4->setOfferLft(offer_lft);
+ }
+
+ // Parse offer-lifetime parameter.
+ Network4Ptr network4 = boost::dynamic_pointer_cast<Network4>(subnet4);
+ parseOfferLft(params, network4);
+
+}
+
+void
+Subnet4ConfigParser::validateResv(const Subnet4Ptr& subnet, ConstHostPtr host) {
+ const IOAddress& address = host->getIPv4Reservation();
+ if (!address.isV4Zero() && !subnet->inRange(address)) {
+ isc_throw(DhcpConfigError, "specified reservation '" << address
+ << "' is not within the IPv4 subnet '"
+ << subnet->toText() << "'");
+ }
+}
+
+boost::shared_ptr<PoolsListParser>
+Subnet4ConfigParser::createPoolsListParser() const {
+ auto parser = boost::make_shared<Pools4ListParser>();
+ return (parser);
+}
+
+//**************************** Subnets4ListConfigParser **********************
+
+Subnets4ListConfigParser::Subnets4ListConfigParser(bool check_iface)
+ : check_iface_(check_iface) {
+}
+
+size_t
+Subnets4ListConfigParser::parse(SrvConfigPtr cfg,
+ ConstElementPtr subnets_list,
+ bool encapsulate_options) {
+ size_t cnt = 0;
+ BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
+
+ auto parser = createSubnetConfigParser();
+ Subnet4Ptr subnet = parser->parse(subnet_json, encapsulate_options);
+ if (subnet) {
+
+ // Adding a subnet to the Configuration Manager may fail if the
+ // subnet id is invalid (duplicate). Thus, we catch exceptions
+ // here to append a position in the configuration string.
+ try {
+ cfg->getCfgSubnets4()->add(subnet);
+ cnt++;
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << subnet_json->getPosition() << ")");
+ }
+ }
+ }
+ return (cnt);
+}
+
+size_t
+Subnets4ListConfigParser::parse(Subnet4Collection& subnets,
+ data::ConstElementPtr subnets_list,
+ bool encapsulate_options) {
+ size_t cnt = 0;
+ BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
+
+ auto parser = createSubnetConfigParser();
+ Subnet4Ptr subnet = parser->parse(subnet_json, encapsulate_options);
+ if (subnet) {
+ try {
+ auto ret = subnets.insert(subnet);
+ if (!ret.second) {
+ isc_throw(Unexpected,
+ "can't store subnet because of conflict");
+ }
+ ++cnt;
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << subnet_json->getPosition() << ")");
+ }
+ }
+ }
+ return (cnt);
+}
+
+boost::shared_ptr<Subnet4ConfigParser>
+Subnets4ListConfigParser::createSubnetConfigParser() const {
+ auto parser = boost::make_shared<Subnet4ConfigParser>(check_iface_);
+ return (parser);
+}
+
+//**************************** Pool6Parser *********************************
+
+PoolPtr
+Pool6Parser::poolMaker(IOAddress &addr, uint32_t len, int32_t ptype)
+{
+ return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
+ (ptype), addr, len)));
+}
+
+PoolPtr
+Pool6Parser::poolMaker(IOAddress &min, IOAddress &max, int32_t ptype)
+{
+ return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
+ (ptype), min, max)));
+}
+
+
+//**************************** Pool6ListParser ***************************
+
+void
+Pools6ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list,
+ bool encapsulate_options) {
+ BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
+ auto parser = createPoolConfigParser();
+ parser->parse(pools, pool, AF_INET6, encapsulate_options);
+ }
+}
+
+boost::shared_ptr<PoolParser>
+Pools6ListParser::createPoolConfigParser() const {
+ auto parser = boost::make_shared<Pool6Parser>();
+ return (parser);
+}
+
+//**************************** PdPoolParser ******************************
+
+PdPoolParser::PdPoolParser() {
+}
+
+void
+PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_,
+ bool encapsulate_options) {
+ checkKeywords(SimpleParser6::PD_POOL6_PARAMETERS, pd_pool_);
+
+ std::string addr_str = getString(pd_pool_, "prefix");
+
+ uint8_t prefix_len = getUint8(pd_pool_, "prefix-len");
+
+ uint8_t delegated_len = getUint8(pd_pool_, "delegated-len");
+
+ std::string excluded_prefix_str = "::";
+ if (pd_pool_->contains("excluded-prefix")) {
+ excluded_prefix_str = getString(pd_pool_, "excluded-prefix");
+ }
+
+ uint8_t excluded_prefix_len = 0;
+ if (pd_pool_->contains("excluded-prefix-len")) {
+ excluded_prefix_len = getUint8(pd_pool_, "excluded-prefix-len");
+ }
+
+ ConstElementPtr user_context = pd_pool_->get("user-context");
+ if (user_context) {
+ user_context_ = user_context;
+ }
+
+ ConstElementPtr client_class = pd_pool_->get("client-class");
+ if (client_class) {
+ client_class_ = client_class;
+ }
+
+ ConstElementPtr class_list = pd_pool_->get("require-client-classes");
+
+ // Check the pool parameters. It will throw an exception if any
+ // of the required parameters are invalid.
+ try {
+ // Attempt to construct the local pool.
+ pool_.reset(new Pool6(IOAddress(addr_str),
+ prefix_len,
+ delegated_len,
+ IOAddress(excluded_prefix_str),
+ excluded_prefix_len));
+ } catch (const std::exception& ex) {
+ // Some parameters don't exist or are invalid. Since we are not
+ // aware whether they don't exist or are invalid, let's append
+ // the position of the pool map element.
+ isc_throw(isc::dhcp::DhcpConfigError, ex.what()
+ << " (" << pd_pool_->getPosition() << ")");
+ }
+
+ // We create subnet first and then parse the options straight into the subnet's
+ // CfgOption structure. Previously, we first parsed the options and then copied
+ // them into the CfgOption after creating the subnet but it had two issues. First,
+ // it cost performance. Second, copying options reset the isEncapsulated() flag.
+ // If the options have been encapsulated we want to preserve the flag to ensure
+ // they are not encapsulated several times.
+ ConstElementPtr option_data = pd_pool_->get("option-data");
+ if (option_data) {
+ auto opts_parser = createOptionDataListParser();
+ opts_parser->parse(pool_->getCfgOption(), option_data, encapsulate_options);
+ }
+
+ if (user_context_) {
+ pool_->setContext(user_context_);
+ }
+
+ if (client_class_) {
+ string cclass = client_class_->stringValue();
+ if (!cclass.empty()) {
+ pool_->allowClientClass(cclass);
+ }
+ }
+
+ 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() << ")");
+ }
+ pool_->requireClientClass((*cclass)->stringValue());
+ }
+ }
+
+ // Add the local pool to the external storage ptr.
+ pools->push_back(pool_);
+}
+
+boost::shared_ptr<OptionDataListParser>
+PdPoolParser::createOptionDataListParser() const {
+ auto parser = boost::make_shared<OptionDataListParser>(AF_INET6);
+ return (parser);
+}
+
+//**************************** PdPoolsListParser ************************
+
+void
+PdPoolsListParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_list) {
+ // Loop through the list of pd pools.
+ BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
+ auto parser = createPdPoolConfigParser();
+ parser->parse(pools, pd_pool);
+ }
+}
+
+boost::shared_ptr<PdPoolParser>
+PdPoolsListParser::createPdPoolConfigParser() const {
+ auto parser = boost::make_shared<PdPoolParser>();
+ return (parser);
+}
+
+//**************************** Subnet6ConfigParser ***********************
+
+Subnet6ConfigParser::Subnet6ConfigParser(bool check_iface)
+ : SubnetConfigParser(AF_INET6, check_iface) {
+}
+
+Subnet6Ptr
+Subnet6ConfigParser::parse(ConstElementPtr subnet, bool encapsulate_options) {
+ // Check parameters.
+ checkKeywords(SimpleParser6::SUBNET6_PARAMETERS, subnet);
+
+ /// Parse all pools first.
+ ConstElementPtr pools = subnet->get("pools");
+ if (pools) {
+ auto parser = createPoolsListParser();
+ parser->parse(pools_, pools, encapsulate_options);
+ }
+ ConstElementPtr pd_pools = subnet->get("pd-pools");
+ if (pd_pools) {
+ auto parser = createPdPoolsListParser();
+ parser->parse(pools_, pd_pools);
+ }
+
+ SubnetPtr generic = SubnetConfigParser::parse(subnet, encapsulate_options);
+
+ if (!generic) {
+ // Sanity check: not supposed to fail.
+ isc_throw(DhcpConfigError,
+ "Failed to create an IPv6 subnet (" <<
+ subnet->getPosition() << ")");
+ }
+
+ Subnet6Ptr sn6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
+ if (!sn6ptr) {
+ // If we hit this, it is a programming error.
+ isc_throw(Unexpected,
+ "Invalid Subnet6 cast in Subnet6ConfigParser::parse");
+ }
+
+ // Set relay information if it was provided
+ if (relay_info_) {
+ sn6ptr->setRelayInfo(*relay_info_);
+ }
+
+ // Parse Host Reservations for this subnet if any.
+ ConstElementPtr reservations = subnet->get("reservations");
+ if (reservations) {
+ HostCollection hosts;
+ HostReservationsListParser<HostReservationParser6> parser;
+ parser.parse(subnet_->getID(), reservations, hosts);
+ for (auto h = hosts.begin(); h != hosts.end(); ++h) {
+ validateResvs(sn6ptr, *h);
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
+ }
+ }
+
+ // Parse allocator specification.
+ auto network = boost::dynamic_pointer_cast<Network>(sn6ptr);
+ parseAllocatorParams(subnet, network);
+
+ // Parse pd-allocator specification.
+ auto network6 = boost::dynamic_pointer_cast<Network6>(sn6ptr);
+ parsePdAllocatorParams(subnet, network6);
+
+ // Instantiate the allocators.
+ sn6ptr->createAllocators();
+
+ return (sn6ptr);
+}
+
+// Unused?
+void
+Subnet6ConfigParser::duplicateOptionWarning(uint32_t code,
+ asiolink::IOAddress& addr) {
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_OPTION_DUPLICATE)
+ .arg(code).arg(addr.toText());
+}
+
+void
+Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
+ asiolink::IOAddress addr, uint8_t len) {
+ // 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
+ // SimpleParser6::setAllDefaults.
+ int64_t subnet_id_max = static_cast<int64_t>(SUBNET_ID_MAX);
+ SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id", 0,
+ subnet_id_max));
+
+ // We want to log whether rapid-commit is enabled, so we get this
+ // before the actual subnet creation.
+ Optional<bool> rapid_commit;
+ if (params->contains("rapid-commit")) {
+ rapid_commit = getBoolean(params, "rapid-commit");
+ }
+
+ // Parse preferred lifetime as it is not parsed by the common function.
+ Triplet<uint32_t> pref = parseIntTriplet(params, "preferred-lifetime");
+
+ // Create a new subnet.
+ auto subnet6 = Subnet6::create(addr, len, Triplet<uint32_t>(),
+ Triplet<uint32_t>(),
+ pref,
+ Triplet<uint32_t>(),
+ subnet_id);
+ subnet_ = subnet6;
+
+ // Move from reservation mode to new reservations flags.
+ ElementPtr mutable_params;
+ mutable_params = boost::const_pointer_cast<Element>(params);
+ // @todo add warning
+ BaseNetworkParser::moveReservationMode(mutable_params);
+
+ // Parse parameters common to all Network derivations.
+ NetworkPtr network = boost::dynamic_pointer_cast<Network>(subnet_);
+ parseCommon(mutable_params, network);
+
+ // Enable or disable Rapid Commit option support for the subnet.
+ if (!rapid_commit.unspecified()) {
+ subnet6->setRapidCommit(rapid_commit);
+ }
+
+ std::ostringstream output;
+ output << addr << "/" << static_cast<int>(len) << " with params: ";
+ // t1 and t2 are optional may be not specified.
+
+ bool has_renew = !subnet6->getT1().unspecified();
+ bool has_rebind = !subnet6->getT2().unspecified();
+ int64_t renew = -1;
+ int64_t rebind = -1;
+
+ if (has_renew) {
+ renew = subnet6->getT1().get();
+ output << "t1=" << renew << ", ";
+ }
+ if (has_rebind) {
+ rebind = subnet6->getT2().get();
+ output << "t2=" << rebind << ", ";
+ }
+
+ if (!subnet6->getPreferred().unspecified()) {
+ output << "preferred-lifetime=" << subnet6->getPreferred().get() << ", ";
+ }
+ if (!subnet6->getValid().unspecified()) {
+ output << "valid-lifetime=" << subnet6->getValid().get();
+ }
+ if (!subnet6->getRapidCommit().unspecified()) {
+ output << ", rapid-commit is "
+ << boolalpha << subnet6->getRapidCommit().get();
+ }
+
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_NEW_SUBNET6).arg(output.str());
+
+ // Get interface-id option content. For now we support string
+ // representation only
+ 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.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"
+ " via relays) cannot be defined at the same time for "
+ "subnet " << addr << "/" << (int)len << "("
+ << params->getPosition() << ")");
+ }
+
+ // Configure interface-id for remote interfaces, if defined
+ 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.unspecified() && !iface.empty()) {
+ if (check_iface_ && !IfaceMgr::instance().getIface(iface)) {
+ ConstElementPtr error = params->get("interface");
+ isc_throw(DhcpConfigError, "Specified network interface name " << iface
+ << " for subnet " << subnet6->toText()
+ << " is not present in the system ("
+ << error->getPosition() << ")");
+ }
+
+ subnet6->setIface(iface);
+ }
+
+ // Try setting up client class.
+ if (params->contains("client-class")) {
+ string client_class = getString(params, "client-class");
+ if (!client_class.empty()) {
+ subnet6->allowClientClass(client_class);
+ }
+ }
+
+ 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());
+ }
+ }
+ }
+
+ /// client-class processing is now generic and handled in the common
+ /// code (see isc::data::SubnetConfigParser::createSubnet)
+
+ // Parse t1-percent and t2-percent
+ parseTeePercents(params, network);
+
+ // Parse DDNS parameters
+ parseDdnsParams(params, network);
+
+ // Parse lease cache parameters
+ parseCacheParams(params, network);
+}
+
+void
+Subnet6ConfigParser::validateResvs(const Subnet6Ptr& subnet, ConstHostPtr host) {
+ IPv6ResrvRange range = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
+ for (auto it = range.first; it != range.second; ++it) {
+ const IOAddress& address = it->second.getPrefix();
+ if (!subnet->inRange(address)) {
+ isc_throw(DhcpConfigError, "specified reservation '" << address
+ << "' is not within the IPv6 subnet '"
+ << subnet->toText() << "'");
+ }
+ }
+}
+
+boost::shared_ptr<PoolsListParser>
+Subnet6ConfigParser::createPoolsListParser() const {
+ auto parser = boost::make_shared<Pools6ListParser>();
+ return (parser);
+}
+
+boost::shared_ptr<PdPoolsListParser>
+Subnet6ConfigParser::createPdPoolsListParser() const {
+ auto parser = boost::make_shared<PdPoolsListParser>();
+ return (parser);
+}
+
+//**************************** Subnet6ListConfigParser ********************
+
+Subnets6ListConfigParser::Subnets6ListConfigParser(bool check_iface)
+ : check_iface_(check_iface) {
+}
+
+size_t
+Subnets6ListConfigParser::parse(SrvConfigPtr cfg,
+ ConstElementPtr subnets_list,
+ bool encapsulate_options) {
+ size_t cnt = 0;
+ BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
+
+ auto parser = createSubnetConfigParser();
+ Subnet6Ptr subnet = parser->parse(subnet_json, encapsulate_options);
+
+ // Adding a subnet to the Configuration Manager may fail if the
+ // subnet id is invalid (duplicate). Thus, we catch exceptions
+ // here to append a position in the configuration string.
+ try {
+ cfg->getCfgSubnets6()->add(subnet);
+ cnt++;
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << subnet_json->getPosition() << ")");
+ }
+ }
+ return (cnt);
+}
+
+size_t
+Subnets6ListConfigParser::parse(Subnet6Collection& subnets,
+ ConstElementPtr subnets_list,
+ bool encapsulate_options) {
+ size_t cnt = 0;
+ BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
+
+ auto parser = createSubnetConfigParser();
+ Subnet6Ptr subnet = parser->parse(subnet_json, encapsulate_options);
+ if (subnet) {
+ try {
+ auto ret = subnets.insert(subnet);
+ if (!ret.second) {
+ isc_throw(Unexpected,
+ "can't store subnet because of conflict");
+ }
+ ++cnt;
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << subnet_json->getPosition() << ")");
+ }
+ }
+ }
+ return (cnt);
+}
+
+boost::shared_ptr<Subnet6ConfigParser>
+Subnets6ListConfigParser::createSubnetConfigParser() const {
+ auto parser = boost::make_shared<Subnet6ConfigParser>(check_iface_);
+ return (parser);
+}
+
+//**************************** D2ClientConfigParser **********************
+
+dhcp_ddns::NameChangeProtocol
+D2ClientConfigParser::getProtocol(ConstElementPtr scope,
+ const std::string& name) {
+ return (getAndConvert<dhcp_ddns::NameChangeProtocol,
+ dhcp_ddns::stringToNcrProtocol>
+ (scope, name, "NameChangeRequest protocol"));
+}
+
+dhcp_ddns::NameChangeFormat
+D2ClientConfigParser::getFormat(ConstElementPtr scope,
+ const std::string& name) {
+ return (getAndConvert<dhcp_ddns::NameChangeFormat,
+ dhcp_ddns::stringToNcrFormat>
+ (scope, name, "NameChangeRequest format"));
+}
+
+D2ClientConfig::ReplaceClientNameMode
+D2ClientConfigParser::getMode(ConstElementPtr scope,
+ const std::string& name) {
+ return (getAndConvert<D2ClientConfig::ReplaceClientNameMode,
+ D2ClientConfig::stringToReplaceClientNameMode>
+ (scope, name, "ReplaceClientName mode"));
+}
+
+D2ClientConfigPtr
+D2ClientConfigParser::parse(isc::data::ConstElementPtr client_config) {
+ D2ClientConfigPtr new_config;
+
+ // Get all parameters that are needed to create the D2ClientConfig.
+ bool enable_updates = getBoolean(client_config, "enable-updates");
+
+ IOAddress server_ip = getAddress(client_config, "server-ip");
+
+ uint32_t server_port = getUint32(client_config, "server-port");
+
+ std::string sender_ip_str = getString(client_config, "sender-ip");
+
+ uint32_t sender_port = getUint32(client_config, "sender-port");
+
+ uint32_t max_queue_size = getUint32(client_config, "max-queue-size");
+
+ dhcp_ddns::NameChangeProtocol ncr_protocol =
+ getProtocol(client_config, "ncr-protocol");
+
+ dhcp_ddns::NameChangeFormat ncr_format =
+ getFormat(client_config, "ncr-format");
+
+ IOAddress sender_ip(0);
+ if (sender_ip_str.empty()) {
+ // The default sender IP depends on the server IP family
+ sender_ip = (server_ip.isV4() ? IOAddress::IPV4_ZERO_ADDRESS() :
+ IOAddress::IPV6_ZERO_ADDRESS());
+ } else {
+ try {
+ sender_ip = IOAddress(sender_ip_str);
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, "invalid address (" << sender_ip_str
+ << ") specified for parameter 'sender-ip' ("
+ << getPosition("sender-ip", client_config) << ")");
+ }
+ }
+
+ // Now we check for logical errors. This repeats what is done in
+ // D2ClientConfig::validate(), but doing it here permits us to
+ // emit meaningful parameter position info in the error.
+ if (ncr_format != dhcp_ddns::FMT_JSON) {
+ isc_throw(D2ClientError, "D2ClientConfig error: NCR Format: "
+ << dhcp_ddns::ncrFormatToString(ncr_format)
+ << " is not supported. ("
+ << getPosition("ncr-format", client_config) << ")");
+ }
+
+ if (ncr_protocol != dhcp_ddns::NCR_UDP) {
+ isc_throw(D2ClientError, "D2ClientConfig error: NCR Protocol: "
+ << dhcp_ddns::ncrProtocolToString(ncr_protocol)
+ << " is not supported. ("
+ << getPosition("ncr-protocol", client_config) << ")");
+ }
+
+ if (sender_ip.getFamily() != server_ip.getFamily()) {
+ isc_throw(D2ClientError,
+ "D2ClientConfig error: address family mismatch: "
+ << "server-ip: " << server_ip.toText()
+ << " is: " << (server_ip.isV4() ? "IPv4" : "IPv6")
+ << " while sender-ip: " << sender_ip.toText()
+ << " is: " << (sender_ip.isV4() ? "IPv4" : "IPv6")
+ << " (" << getPosition("sender-ip", client_config) << ")");
+ }
+
+ if (server_ip == sender_ip && server_port == sender_port) {
+ isc_throw(D2ClientError,
+ "D2ClientConfig error: server and sender cannot"
+ " share the exact same IP address/port: "
+ << server_ip.toText() << "/" << server_port
+ << " (" << getPosition("sender-ip", client_config) << ")");
+ }
+
+ try {
+ // Attempt to create the new client config.
+ new_config.reset(new D2ClientConfig(enable_updates,
+ server_ip,
+ server_port,
+ sender_ip,
+ sender_port,
+ max_queue_size,
+ ncr_protocol,
+ ncr_format));
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << client_config->getPosition() << ")");
+ }
+
+ // Add user context
+ ConstElementPtr user_context = client_config->get("user-context");
+ if (user_context) {
+ new_config->setContext(user_context);
+ }
+
+ return (new_config);
+}
+
+/// @brief This table defines default values for D2 client configuration
+const SimpleDefaults D2ClientConfigParser::D2_CLIENT_CONFIG_DEFAULTS = {
+ // enable-updates is unconditionally required
+ { "server-ip", Element::string, "127.0.0.1" },
+ { "server-port", Element::integer, "53001" },
+ // default sender-ip depends on server-ip family, so we leave default blank
+ // parser knows to use the appropriate ZERO address based on server-ip
+ { "sender-ip", Element::string, "" },
+ { "sender-port", Element::integer, "0" },
+ { "max-queue-size", Element::integer, "1024" },
+ { "ncr-protocol", Element::string, "UDP" },
+ { "ncr-format", Element::string, "JSON" }
+};
+
+size_t
+D2ClientConfigParser::setAllDefaults(isc::data::ConstElementPtr d2_config) {
+ ElementPtr mutable_d2 = boost::const_pointer_cast<Element>(d2_config);
+ return (SimpleParser::setDefaults(mutable_d2, D2_CLIENT_CONFIG_DEFAULTS));
+}
+
+} // namespace dhcp
+} // namespace isc
diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h
new file mode 100644
index 0000000..b416539
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h
@@ -0,0 +1,1079 @@
+// Copyright (C) 2013-2023 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 DHCP_PARSERS_H
+#define DHCP_PARSERS_H
+
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcp/option_definition.h>
+#include <dhcp/option_space_container.h>
+#include <dhcpsrv/d2_client_cfg.h>
+#include <dhcpsrv/cfg_iface.h>
+#include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/network.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/cfg_option_def.h>
+#include <dhcpsrv/cfg_mac_source.h>
+#include <dhcpsrv/srv_config.h>
+#include <dhcpsrv/parsers/base_network_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <cc/simple_parser.h>
+#include <exceptions/exceptions.h>
+#include <util/optional.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// Collection of containers holding option spaces. Each container within
+/// a particular option space holds so-called option descriptors.
+typedef OptionSpaceContainer<OptionContainer, OptionDescriptor,
+ std::string> OptionStorage;
+/// @brief Shared pointer to option storage.
+typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
+
+/// @brief A template class that stores named elements of a given data type.
+///
+/// This template class is provides data value storage for configuration
+/// parameters of a given data type. The values are stored by parameter name
+/// and as instances of type "ValueType". Each value held in the storage has
+/// a corresponding position within a configuration string (file) specified
+/// as a: file name, line number and position within the line. The position
+/// information is used for logging when the particular configuration value
+/// causes a configuration error.
+///
+/// @tparam ValueType is the data type of the elements to store.
+template<typename ValueType>
+class ValueStorage {
+public:
+ /// @brief Stores the parameter, its value and the position in the
+ /// store.
+ ///
+ /// If the parameter does not exist in the store, then it will be added,
+ /// otherwise its data value and the position will be updated with the
+ /// given values.
+ ///
+ /// @param name is the name of the parameter to store.
+ /// @param value is the data value to store.
+ /// @param position is the position of the data element within a
+ /// configuration string (file).
+ void setParam(const std::string& name, const ValueType& value,
+ const data::Element::Position& position) {
+ values_[name] = value;
+ positions_[name] = position;
+ }
+
+ /// @brief Returns the data value for the given parameter.
+ ///
+ /// Finds and returns the data value for the given parameter.
+ /// @param name is the name of the parameter for which the data
+ /// value is desired.
+ ///
+ /// @return The parameter's data value of type @c ValueType.
+ /// @throw DhcpConfigError if the parameter is not found.
+ ValueType getParam(const std::string& name) const {
+ typename std::map<std::string, ValueType>::const_iterator param
+ = values_.find(name);
+
+ if (param == values_.end()) {
+ isc_throw(DhcpConfigError, "Missing parameter '"
+ << name << "'");
+ }
+
+ return (param->second);
+ }
+
+ /// @brief Returns position of the data element in the configuration string.
+ ///
+ /// The returned object comprises file name, line number and the position
+ /// within the particular line of the configuration string where the data
+ /// element holding a particular value is located.
+ ///
+ /// @param name is the name of the parameter which position is desired.
+ /// @param parent Pointer to a data element which position should be
+ /// returned when position of the specified parameter is not found.
+ ///
+ /// @return Position of the data element or the position holding empty
+ /// file name and two zeros if the position hasn't been specified for the
+ /// particular value.
+ const data::Element::Position&
+ getPosition(const std::string& name, const data::ConstElementPtr parent =
+ data::ConstElementPtr()) const {
+ typename std::map<std::string, data::Element::Position>::const_iterator
+ pos = positions_.find(name);
+ if (pos == positions_.end()) {
+ return (parent ? parent->getPosition() :
+ data::Element::ZERO_POSITION());
+ }
+
+ return (pos->second);
+ }
+
+ /// @brief Returns the data value for an optional parameter.
+ ///
+ /// Finds and returns the data value for the given parameter or
+ /// a supplied default value if it is not found.
+ ///
+ /// @param name is the name of the parameter for which the data
+ /// value is desired.
+ /// @param default_value value to use the default
+ ///
+ /// @return The parameter's data value of type @c ValueType.
+ ValueType getOptionalParam(const std::string& name,
+ const ValueType& default_value) const {
+ typename std::map<std::string, ValueType>::const_iterator param
+ = values_.find(name);
+
+ if (param == values_.end()) {
+ return (default_value);
+ }
+
+ return (param->second);
+ }
+
+ /// @brief Remove the parameter from the store.
+ ///
+ /// Deletes the entry for the given parameter from the store if it
+ /// exists.
+ ///
+ /// @param name is the name of the parameter to delete.
+ void delParam(const std::string& name) {
+ values_.erase(name);
+ positions_.erase(name);
+ }
+
+ /// @brief Deletes all of the entries from the store.
+ ///
+ void clear() {
+ values_.clear();
+ positions_.clear();
+ }
+
+private:
+ /// @brief An std::map of the data values, keyed by parameter names.
+ std::map<std::string, ValueType> values_;
+
+ /// @brief An std::map holding positions of the data elements in the
+ /// configuration, which values are held in @c values_.
+ ///
+ /// The position is used for logging, when the particular value
+ /// causes a configuration error.
+ std::map<std::string, data::Element::Position> positions_;
+
+};
+
+/// @brief Combination of parameter name and configuration contents
+typedef std::pair<std::string, isc::data::ConstElementPtr> ConfigPair;
+
+/// @brief a collection of elements that store uint32 values
+typedef ValueStorage<uint32_t> Uint32Storage;
+typedef boost::shared_ptr<Uint32Storage> Uint32StoragePtr;
+
+/// @brief a collection of elements that store string values
+typedef ValueStorage<std::string> StringStorage;
+typedef boost::shared_ptr<StringStorage> StringStoragePtr;
+
+/// @brief Storage for parsed boolean values.
+typedef ValueStorage<bool> BooleanStorage;
+typedef boost::shared_ptr<BooleanStorage> BooleanStoragePtr;
+
+/// @brief parser for MAC/hardware acquisition sources
+///
+/// This parser handles Dhcp6/mac-sources entry.
+/// It contains a list of MAC/hardware acquisition source, i.e. methods how
+/// MAC address can possibly by obtained in DHCPv6. For a currently supported
+/// methods, see @ref isc::dhcp::Pkt::getMAC.
+class MACSourcesListConfigParser : public isc::data::SimpleParser {
+public:
+ /// @brief parses parameters value
+ ///
+ /// Parses configuration entry (list of sources) and adds each element
+ /// to the sources list.
+ ///
+ /// @param value pointer to the content of parsed values
+ /// @param mac_sources parsed sources will be stored here
+ void parse(CfgMACSource& mac_sources, isc::data::ConstElementPtr value);
+};
+
+/// @brief Parser for the control-socket structure
+///
+/// It does not parse anything, simply stores the element in
+/// the staging config.
+class ControlSocketParser : public isc::data::SimpleParser {
+public:
+ /// @brief "Parses" control-socket structure
+ ///
+ /// Since the SrvConfig structure takes the socket definition
+ /// as ConstElementPtr, there's really nothing to parse here.
+ /// It only does basic sanity checks and throws DhcpConfigError
+ /// if the value is null or is not a map.
+ ///
+ /// @param srv_cfg parsed values will be stored here
+ /// @param value pointer to the content of parsed values
+ void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value);
+};
+
+/// @brief Parser for a single option definition.
+///
+/// This parser creates an instance of a single option definition.
+class OptionDefParser : public isc::data::SimpleParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param address_family Address family: @c AF_INET or @c AF_INET6.
+ OptionDefParser(const uint16_t address_family);
+
+ /// @brief Parses an entry that describes single option definition.
+ ///
+ /// @param option_def a configuration entry to be parsed.
+ /// @return option definition of the parsed structure.
+ ///
+ /// @throw DhcpConfigError if parsing was unsuccessful.
+ OptionDefinitionPtr parse(isc::data::ConstElementPtr option_def);
+
+private:
+ /// @brief Address family: @c AF_INET or @c AF_INET6.
+ uint16_t address_family_;
+};
+
+/// @brief Parser for a list of option definitions.
+///
+/// This parser iterates over all configuration entries that define
+/// option definitions and creates instances of these definitions.
+/// If the parsing is successful, the collection of created definitions
+/// is put into the provided storage.
+class OptionDefListParser : public isc::data::SimpleParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param address_family Address family: @c AF_INET or @c AF_INET6.
+ OptionDefListParser(const uint16_t address_family);
+
+ /// @brief Parses a list of option definitions, create them and store in cfg
+ ///
+ /// This method iterates over def_list, which is a JSON list of option definitions,
+ /// then creates corresponding option definitions and store them in the
+ /// configuration structure.
+ ///
+ /// @param def_list JSON list describing option definitions
+ /// @param cfg parsed option definitions will be stored here
+ void parse(CfgOptionDefPtr cfg, isc::data::ConstElementPtr def_list);
+
+private:
+ /// @brief Address family: @c AF_INET or @c AF_INET6.
+ uint16_t address_family_;
+};
+
+/// @brief a collection of pools
+///
+/// That type is used as intermediate storage, when pools are parsed, but there is
+/// no subnet object created yet to store them.
+typedef std::vector<PoolPtr> PoolStorage;
+typedef boost::shared_ptr<PoolStorage> PoolStoragePtr;
+
+/// @brief parser for a single pool definition
+///
+/// This abstract parser handles pool definitions, i.e. a list of entries of one
+/// of two syntaxes: min-max and prefix/len. Pool objects are created
+/// and stored in chosen PoolStorage container.
+///
+/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[X]/pools[X] structure.
+class PoolParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief destructor.
+ virtual ~PoolParser() {
+ }
+
+ /// @brief parses the actual structure
+ ///
+ /// This method parses the actual list of interfaces.
+ /// No validation is done at this stage, everything is interpreted as
+ /// interface name.
+ /// @param pools is the storage in which to store the parsed pool
+ /// @param pool_structure a single entry on a list of pools
+ /// @param address_family AF_INET (for DHCPv4) or AF_INET6 (for DHCPv6).
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+ virtual void parse(PoolStoragePtr pools,
+ isc::data::ConstElementPtr pool_structure,
+ const uint16_t address_family,
+ bool encapsulate_options = true);
+
+protected:
+ /// @brief Creates a Pool object given a IPv4 prefix and the prefix length.
+ ///
+ /// @param addr is the IP prefix of the pool.
+ /// @param len is the prefix length.
+ /// @param ptype is the type of pool to create.
+ /// @return returns a PoolPtr to the new Pool object.
+ virtual PoolPtr poolMaker(isc::asiolink::IOAddress &addr, uint32_t len,
+ int32_t ptype = 0) = 0;
+
+ /// @brief Creates a Pool object given starting and ending IP addresses.
+ ///
+ /// @param min is the first IP address in the pool.
+ /// @param max is the last IP address in the pool.
+ /// @param ptype is the type of pool to create (not used by all derivations)
+ /// @return returns a PoolPtr to the new Pool object.
+ virtual PoolPtr poolMaker(isc::asiolink::IOAddress &min,
+ isc::asiolink::IOAddress &max,
+ int32_t ptype = 0) = 0;
+
+ /// @brief Returns an instance of the @c OptionDataListParser to
+ /// be used in parsing the option-data structure.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for option data.
+ ///
+ /// @param address_family AF_INET (for DHCPv4) or AF_INET6 (for DHCPv6).
+ ///
+ /// @return an instance of the @c OptionDataListParser.
+ virtual boost::shared_ptr<OptionDataListParser>
+ createOptionDataListParser(const uint16_t address_family) const;
+};
+
+/// @brief Parser for IPv4 pool definitions.
+///
+/// This is the IPv4 derivation of the PoolParser class and handles pool
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
+/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
+/// PoolStorage container.
+///
+/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
+class Pool4Parser : public PoolParser {
+protected:
+ /// @brief Creates a Pool4 object given a IPv4 prefix and the prefix length.
+ ///
+ /// @param addr is the IPv4 prefix of the pool.
+ /// @param len is the prefix length.
+ /// @param ignored dummy parameter to provide symmetry between the
+ /// PoolParser derivations. The V6 derivation requires a third value.
+ /// @return returns a PoolPtr to the new Pool4 object.
+ PoolPtr poolMaker (asiolink::IOAddress &addr, uint32_t len,
+ int32_t ignored);
+
+ /// @brief Creates a Pool4 object given starting and ending IPv4 addresses.
+ ///
+ /// @param min is the first IPv4 address in the pool.
+ /// @param max is the last IPv4 address in the pool.
+ /// @param ignored dummy parameter to provide symmetry between the
+ /// PoolParser derivations. The V6 derivation requires a third value.
+ /// @return returns a PoolPtr to the new Pool4 object.
+ PoolPtr poolMaker (asiolink::IOAddress &min, asiolink::IOAddress &max,
+ int32_t ignored);
+};
+
+/// @brief Parser for a list of pools
+///
+/// This parser parses a list pools. Each element on that list gets its own
+/// parser, created with poolParserMaker() method. That method must be specified
+/// for each protocol family (v4 or v6) separately.
+class PoolsListParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief destructor.
+ virtual ~PoolsListParser() {
+ }
+
+ /// @brief parses the actual structure
+ ///
+ /// This method parses the actual list of pools.
+ ///
+ /// @param pools is the storage in which to store the parsed pools.
+ /// @param pools_list a list of pool structures
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+ virtual void parse(PoolStoragePtr pools,
+ isc::data::ConstElementPtr pools_list,
+ bool encapsulate_options) = 0;
+
+protected:
+
+ /// @brief Returns an instance of the @c PoolParser to be used in
+ /// parsing the address pools.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the pools.
+ ///
+ /// @return an instance of the @c PoolParser.
+ virtual boost::shared_ptr<PoolParser> createPoolConfigParser() const = 0;
+};
+
+/// @brief Specialization of the pool list parser for DHCPv4
+class Pools4ListParser : public PoolsListParser {
+public:
+
+ /// @brief parses the actual structure
+ ///
+ /// This method parses the actual list of pools.
+ ///
+ /// @param pools storage container in which to store the parsed pool.
+ /// @param pools_list a list of pool structures
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+ void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list,
+ bool encapsulate_options = true);
+
+protected:
+
+ /// @brief Returns an instance of the @c Pool4Parser to be used in
+ /// parsing the address pools.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the pools.
+ ///
+ /// @return an instance of the @c Pool4Parser.
+ virtual boost::shared_ptr<PoolParser> createPoolConfigParser() const;
+};
+
+/// @brief parser for additional relay information
+///
+/// This concrete parser handles RelayInfo structure definitions.
+/// So far that structure holds only relay IP (v4 or v6) address, but it
+/// is expected that the number of parameters will increase over time.
+///
+/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[x]/relay parameters.
+class RelayInfoParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief constructor
+ /// @param family specifies protocol family (IPv4 or IPv6)
+ explicit RelayInfoParser(const isc::dhcp::Option::Universe& family);
+
+ /// @brief parses the actual relay parameters
+ ///
+ /// The elements currently supported are:
+ /// -# ip-address
+ /// -# ip-addresses
+ ///
+ /// Note that ip-address and ip-addresses are mutually exclusive, with
+ /// former being deprecated. The use of ip-address will cause an debug
+ /// log to be emitted, reminded users to switch.
+ ///
+ /// @param relay_info configuration will be stored here
+ /// @param relay_elem Element tree containing the relay and its members
+ /// @throw isc::dhcp::DhcpConfigError if both or neither of ip-address
+ /// and ip-addresses are specified.
+ void parse(const isc::dhcp::Network::RelayInfoPtr& relay_info,
+ isc::data::ConstElementPtr relay_elem);
+
+ /// @brief Attempts to add an IP address to list of relay addresses
+ ///
+ /// @param name name of the element supplying the address string, (either
+ /// "ip-address" or "ip-addresses")
+ /// @param address_str string form of the IP address to add
+ /// @param relay_elem parent relay element (needed for position info)
+ /// @param relay_info RelayInfo to which the address should be added
+ /// @throw isc::dhcp::DhcpConfigError if the address string is not a valid
+ /// IP address, is an address of the wrong family, or is already in the
+ /// relay address list
+ void addAddress(const std::string& name, const std::string& address_str,
+ isc::data::ConstElementPtr relay_elem,
+ const isc::dhcp::Network::RelayInfoPtr& relay_info);
+private:
+
+ /// Protocol family (IPv4 or IPv6)
+ Option::Universe family_;
+};
+
+/// @brief this class parses a single subnet
+///
+/// There are dedicated @ref Subnet4ConfigParser and @ref Subnet6ConfigParser
+/// classes. They provide specialized parse() methods that return Subnet4Ptr
+/// or Subnet6Ptr.
+///
+/// This class parses the whole subnet definition. This class attempts to
+/// unify the code between v4 and v6 as much as possible. As a result, the flow
+/// is somewhat complex and it looks as follows:
+///
+/// ------- Base class
+/// /
+/// | /----- Derived class
+/// 1. * SubnetXConfigParser::parse() is called.
+/// 2. * SubnetConfigParser::parse() is called.
+/// 3. * SubnetConfigParser::createSubnet() is called.
+/// 4. * SubnetXConfigParser::initSubnet() is called (Subnet4 or Subnet6 is
+/// instantiated here and family specific parameters are set)
+/// 5. Control returns to createSubnet() (step 3) and common parameters
+/// are set.
+class SubnetConfigParser : public BaseNetworkParser {
+public:
+
+ /// @brief constructor
+ ///
+ /// @param family address family: @c AF_INET or @c AF_INET6
+ /// @param check_iface Check if the specified interface exists in
+ /// the system.
+ explicit SubnetConfigParser(uint16_t family, bool check_iface = true);
+
+ /// @brief virtual destructor (does nothing)
+ virtual ~SubnetConfigParser() { }
+
+protected:
+ /// @brief parses a subnet description and returns Subnet{4,6} structure
+ ///
+ /// This method is called from specialized (Subnet4ConfigParser or
+ /// Subnet6ConfigParser) classes.
+ ///
+ /// @param subnet pointer to the content of subnet definition
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @return a pointer to newly created subnet
+ ///
+ /// @throw isc::DhcpConfigError if subnet configuration parsing failed.
+ SubnetPtr parse(isc::data::ConstElementPtr subnet,
+ bool encapsulate_options);
+
+ /// @brief Instantiates the subnet based on a given IP prefix and prefix
+ /// length.
+ ///
+ /// @param params configuration parameters for that subnet
+ /// @param addr is the IP prefix of the subnet.
+ /// @param len is the prefix length
+ virtual void initSubnet(isc::data::ConstElementPtr params,
+ isc::asiolink::IOAddress addr, uint8_t len) = 0;
+
+protected:
+
+ /// @brief Create a new subnet using a data from child parsers.
+ ///
+ /// @param data Element map that describes the subnet
+ /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing
+ /// failed.
+ void createSubnet(isc::data::ConstElementPtr data);
+
+ /// @brief Returns an instance of the @c OptionDataListParser to
+ /// be used in parsing the option-data structure.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for option data.
+ ///
+ /// @return an instance of the @c OptionDataListParser.
+ virtual boost::shared_ptr<OptionDataListParser> createOptionDataListParser() const;
+
+ /// @brief Returns an instance of the @c PoolsListParser to be used
+ /// in parsing the address pools.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the pools.
+ ///
+ /// @return an instance of the @c PoolsListParser.
+ virtual boost::shared_ptr<PoolsListParser>
+ createPoolsListParser() const = 0;
+
+ /// Storage for pools belonging to this subnet.
+ PoolStoragePtr pools_;
+
+ /// Pointer to the created subnet object.
+ isc::dhcp::SubnetPtr subnet_;
+
+ /// @brief Address family: @c AF_INET or @c AF_INET6
+ uint16_t address_family_;
+
+ /// Pointer to relay information
+ isc::dhcp::Network::RelayInfoPtr relay_info_;
+
+ /// Check if the specified interface exists in the system.
+ bool check_iface_;
+};
+
+/// @anchor Subnet4ConfigParser
+/// @brief This class parses a single IPv4 subnet.
+///
+/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
+/// parameters as needed.
+class Subnet4ConfigParser : public SubnetConfigParser {
+public:
+ /// @brief Constructor
+ ///
+ /// stores global scope parameters, options, option definitions.
+ ///
+ /// @param check_iface Check if the specified interface exists in
+ /// the system.
+ Subnet4ConfigParser(bool check_iface = true);
+
+ /// @brief Parses a single IPv4 subnet configuration and adds to the
+ /// Configuration Manager.
+ ///
+ /// @param subnet A new subnet being configured.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @return a pointer to created Subnet4 object
+ Subnet4Ptr parse(data::ConstElementPtr subnet,
+ bool encapsulate_options = true);
+
+protected:
+
+ /// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
+ /// and prefix length.
+ ///
+ /// @param params Data structure describing a subnet.
+ /// @param addr is IPv4 address of the subnet.
+ /// @param len is the prefix length
+ void initSubnet(data::ConstElementPtr params,
+ asiolink::IOAddress addr, uint8_t len);
+
+ /// @brief Verifies the host reservation address is in the subnet range
+ ///
+ /// @param subnet pointer to the subnet
+ /// @param host pointer to the host reservation
+ /// @throw DhcpConfigError when the address is not in the subnet range.
+ void validateResv(const Subnet4Ptr& subnet, ConstHostPtr host);
+
+ /// @brief Returns an instance of the @c Pools4ListParser to be used
+ /// in parsing the address pools.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the pools.
+ ///
+ /// @return an instance of the @c Pools4ListParser.
+ virtual boost::shared_ptr<PoolsListParser>
+ createPoolsListParser() const;
+};
+
+/// @brief this class parses list of DHCP4 subnets
+///
+/// This is a wrapper parser that handles the whole list of Subnet4
+/// definitions. It iterates over all entries and creates Subnet4ConfigParser
+/// for each entry.
+class Subnets4ListConfigParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief constructor
+ ///
+ /// @param check_iface Check if the specified interface exists in
+ /// the system.
+ Subnets4ListConfigParser(bool check_iface = true);
+
+ /// @brief Virtual destructor.
+ virtual ~Subnets4ListConfigParser() {
+ }
+
+ /// @brief parses contents of the list
+ ///
+ /// Iterates over all entries on the list, parses its content
+ /// (by instantiating Subnet6ConfigParser) and adds to specified
+ /// configuration.
+ ///
+ /// @param cfg Pointer to server configuration.
+ /// @param subnets_list pointer to a list of IPv4 subnets
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @return number of subnets created
+ size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list,
+ bool encapsulate_options = true);
+
+ /// @brief Parses contents of the subnet4 list.
+ ///
+ /// @param [out] subnets Container where parsed subnets will be stored.
+ /// @param subnets_list pointer to a list of IPv4 subnets
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @return Number of subnets created.
+ size_t parse(Subnet4Collection& subnets,
+ data::ConstElementPtr subnets_list,
+ bool encapsulate_options = true);
+
+protected:
+
+ /// @brief Returns an instance of the @c Subnet4ConfigParser to be
+ /// used in parsing the subnets.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the subnets.
+ ///
+ /// @return an instance of the @c Subnet4ConfigParser.
+ virtual boost::shared_ptr<Subnet4ConfigParser> createSubnetConfigParser() const;
+
+ /// Check if the specified interface exists in the system.
+ bool check_iface_;
+};
+
+/// @brief Parser for IPv6 pool definitions.
+///
+/// This is the IPv6 derivation of the PoolParser class and handles pool
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
+/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
+/// PoolStorage container.
+///
+/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
+class Pool6Parser : public PoolParser {
+protected:
+ /// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
+ ///
+ /// @param addr is the IPv6 prefix of the pool.
+ /// @param len is the prefix length.
+ /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
+ /// passed in as an int32_t and cast to PoolType to accommodate a
+ /// polymorphic interface.
+ /// @return returns a PoolPtr to the new Pool4 object.
+ PoolPtr poolMaker (asiolink::IOAddress &addr, uint32_t len, int32_t ptype);
+
+ /// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
+ ///
+ /// @param min is the first IPv6 address in the pool.
+ /// @param max is the last IPv6 address in the pool.
+ /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
+ /// passed in as an int32_t and cast to PoolType to accommodate a
+ /// polymorphic interface.
+ /// @return returns a PoolPtr to the new Pool4 object.
+ PoolPtr poolMaker (asiolink::IOAddress &min, asiolink::IOAddress &max,
+ int32_t ptype);
+};
+
+/// @brief Specialization of the pool list parser for DHCPv6
+class Pools6ListParser : public PoolsListParser {
+public:
+
+ /// @brief parses the actual structure
+ ///
+ /// This method parses the actual list of pools.
+ ///
+ /// @param pools storage container in which to store the parsed pool.
+ /// @param pools_list a list of pool structures
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+ void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list,
+ bool encapsulate_options = true);
+
+protected:
+
+ /// @brief Returns an instance of the @c Pool6Parser to be used in
+ /// parsing the address pools.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the pools.
+ ///
+ /// @return an instance of the @c Pool6Parser.
+ virtual boost::shared_ptr<PoolParser> createPoolConfigParser() const;
+};
+
+/// @brief Parser for IPv6 prefix delegation definitions.
+///
+/// This class handles prefix delegation pool definitions for IPv6 subnets
+/// Pool6 objects are created and stored in the given PoolStorage container.
+///
+/// PdPool definitions currently support three elements: prefix, prefix-len,
+/// and delegated-len, as shown in the example JSON text below:
+///
+/// @code
+///
+/// {
+/// "prefix": "2001:db8:1::",
+/// "prefix-len": 64,
+/// "delegated-len": 128
+/// }
+/// @endcode
+///
+class PdPoolParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Constructor.
+ ///
+ PdPoolParser();
+
+ /// @brief Virtual destructor.
+ virtual ~PdPoolParser() {
+ }
+
+ /// @brief Builds a prefix delegation pool from the given configuration
+ ///
+ /// This function parses configuration entries and creates an instance
+ /// of a dhcp::Pool6 configured for prefix delegation.
+ ///
+ /// @param pools storage container in which to store the parsed pool.
+ /// @param pd_pool_ pointer to an element that holds configuration entries
+ /// that define a prefix delegation pool.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ ///
+ /// @throw DhcpConfigError if configuration parsing fails.
+ void parse(PoolStoragePtr pools, data::ConstElementPtr pd_pool_,
+ bool encapsulate_options = true);
+
+protected:
+
+ /// @brief Returns an instance of the @c OptionDataListParser to
+ /// be used in parsing the option-data structure.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for option data.
+ ///
+ /// @return an instance of the @c OptionDataListParser.
+ virtual boost::shared_ptr<OptionDataListParser>
+ createOptionDataListParser() const;
+
+ /// Pointer to the created pool object.
+ isc::dhcp::Pool6Ptr pool_;
+
+ /// @brief User context (optional, may be null)
+ ///
+ /// User context is arbitrary user data, to be used by hooks.
+ isc::data::ConstElementPtr user_context_;
+
+ /// @brief Client class (a client has to belong to to use this pd-pool)
+ ///
+ /// If null, everyone is allowed.
+ isc::data::ConstElementPtr client_class_;
+};
+
+/// @brief Parser for a list of prefix delegation pools.
+///
+/// This parser iterates over a list of prefix delegation pool entries and
+/// creates pool instances for each one. If the parsing is successful, the
+/// collection of pools is committed to the provided storage.
+class PdPoolsListParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Virtual destructor.
+ virtual ~PdPoolsListParser() {
+ }
+
+ /// @brief Parse configuration entries.
+ ///
+ /// This function parses configuration entries and creates instances
+ /// of prefix delegation pools .
+ ///
+ /// @param pools is the pool storage in which to store the parsed
+ /// @param pd_pool_list pointer to an element that holds entries
+ /// that define a prefix delegation pool.
+ ///
+ /// @throw DhcpConfigError if configuration parsing fails.
+ void parse(PoolStoragePtr pools, data::ConstElementPtr pd_pool_list);
+
+protected:
+
+ /// @brief Returns an instance of the @c PdPoolParser to be used in
+ /// parsing the prefix delegation pools.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the pools.
+ ///
+ /// @return an instance of the @c PdPool6Parser.
+ virtual boost::shared_ptr<PdPoolParser>
+ createPdPoolConfigParser() const;
+};
+
+/// @anchor Subnet6ConfigParser
+/// @brief This class parses a single IPv6 subnet.
+///
+/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
+/// parameters as needed.
+class Subnet6ConfigParser : public SubnetConfigParser {
+public:
+
+ /// @brief Constructor
+ ///
+ /// stores global scope parameters, options, option definitions.
+ ///
+ /// @param check_iface Check if the specified interface exists in
+ /// the system.
+ Subnet6ConfigParser(bool check_iface = true);
+
+ /// @brief Parses a single IPv6 subnet configuration and adds to the
+ /// Configuration Manager.
+ ///
+ /// @param subnet A new subnet being configured.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @return a pointer to created Subnet6 object
+ Subnet6Ptr parse(data::ConstElementPtr subnet,
+ bool encapsulate_options = true);
+
+protected:
+ /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
+ /// options.
+ ///
+ /// @param code is the numeric option code of the duplicate option
+ /// @param addr is the subnet address
+ /// @todo A means to know the correct logger and perhaps a common
+ /// message would allow this message to be emitted by the base class.
+ virtual void duplicateOptionWarning(uint32_t code,
+ asiolink::IOAddress& addr);
+
+ /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
+ /// and prefix length.
+ ///
+ /// @param params Data structure describing a subnet.
+ /// @param addr is IPv6 prefix of the subnet.
+ /// @param len is the prefix length
+ void initSubnet(isc::data::ConstElementPtr params,
+ isc::asiolink::IOAddress addr, uint8_t len);
+
+ /// @brief Verifies host reservation addresses are in the subnet range
+ ///
+ /// @param subnet pointer to the subnet
+ /// @param host pointer to the host reservation
+ /// @throw DhcpConfigError when an address is not in the subnet range.
+ void validateResvs(const Subnet6Ptr& subnet, ConstHostPtr host);
+
+ /// @brief Returns an instance of the @c Pools6ListParser to be used
+ /// in parsing the address pools.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the pools.
+ ///
+ /// @return an instance of the @c Pools6ListParser.
+ virtual boost::shared_ptr<PoolsListParser>
+ createPoolsListParser() const;
+
+ /// @brief Returns an instance of the @c PdPools6ListParser to be used
+ /// in parsing the prefix delegation pools.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the pools.
+ ///
+ /// @return an instance of the @c PdPools6ListParser.
+ virtual boost::shared_ptr<PdPoolsListParser>
+ createPdPoolsListParser() const;
+};
+
+
+/// @brief this class parses a list of DHCP6 subnets
+///
+/// This is a wrapper parser that handles the whole list of Subnet6
+/// definitions. It iterates over all entries and creates Subnet6ConfigParser
+/// for each entry.
+class Subnets6ListConfigParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief constructor
+ ///
+ /// @param check_iface Check if the specified interface exists in
+ /// the system.
+ Subnets6ListConfigParser(bool check_iface = true);
+
+ /// @brief Virtual destructor.
+ virtual ~Subnets6ListConfigParser() {
+ }
+
+ /// @brief parses contents of the list
+ ///
+ /// Iterates over all entries on the list, parses its content
+ /// (by instantiating Subnet6ConfigParser) and adds to specified
+ /// configuration.
+ ///
+ /// @param cfg configuration (parsed subnets will be stored here)
+ /// @param subnets_list pointer to a list of IPv6 subnets
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ /// @throw DhcpConfigError if CfgMgr rejects the subnet (e.g. subnet-id is a duplicate)
+ size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list,
+ bool encapsulate_options = true);
+
+ /// @brief Parses contents of the subnet6 list.
+ ///
+ /// @param [out] subnets Container where parsed subnets will be stored.
+ /// @param subnets_list pointer to a list of IPv6 subnets
+ /// @return Number of subnets created.
+ size_t parse(Subnet6Collection& subnets,
+ data::ConstElementPtr subnets_list,
+ bool encapsulate_options = true);
+
+protected:
+
+ /// @brief Returns an instance of the @c Subnet6ConfigParser to be
+ /// used in parsing the subnets.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the subnets.
+ ///
+ /// @return an instance of the @c Subnet6ConfigParser.
+ virtual boost::shared_ptr<Subnet6ConfigParser> createSubnetConfigParser() const;
+
+ /// Check if the specified interface exists in the system.
+ bool check_iface_;
+};
+
+/// @brief Parser for D2ClientConfig
+///
+/// This class parses the configuration element "dhcp-ddns" common to the
+/// config files for both dhcp4 and dhcp6. It creates an instance of a
+/// D2ClientConfig.
+class D2ClientConfigParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Parses a given dhcp-ddns element into D2ClientConfig.
+ ///
+ /// @param d2_client_cfg is the "dhcp-ddns" configuration to parse
+ ///
+ /// The elements currently supported are (see isc::dhcp::D2ClientConfig
+ /// for details on each):
+ /// -# enable-updates
+ /// -# server-ip
+ /// -# server-port
+ /// -# sender-ip
+ /// -# sender-port
+ /// -# max-queue-size
+ /// -# ncr-protocol
+ /// -# ncr-format
+ ///
+ /// @return returns a pointer to newly created D2ClientConfig.
+ D2ClientConfigPtr parse(isc::data::ConstElementPtr d2_client_cfg);
+
+ /// @brief Defaults for the D2 client configuration.
+ static const isc::data::SimpleDefaults D2_CLIENT_CONFIG_DEFAULTS;
+
+ /// @brief Sets all defaults for D2 client configuration.
+ ///
+ /// This method sets defaults value. It must not be called
+ /// before the short cut disabled updates condition was checked.
+ ///
+ /// @param d2_config d2 client configuration (will be const cast
+ // to ElementPtr)
+ /// @return number of parameters inserted
+ static size_t setAllDefaults(isc::data::ConstElementPtr d2_config);
+
+private:
+
+ /// @brief Returns a value converted to NameChangeProtocol
+ ///
+ /// Instantiation of getAndConvert() to NameChangeProtocol
+ ///
+ /// @param scope specified parameter will be extracted from this scope
+ /// @param name name of the parameter
+ /// @return a NameChangeProtocol value
+ dhcp_ddns::NameChangeProtocol
+ getProtocol(isc::data::ConstElementPtr scope, const std::string& name);
+
+ /// @brief Returns a value converted to NameChangeFormat
+ ///
+ /// Instantiation of getAndConvert() to NameChangeFormat
+ ///
+ /// @param scope specified parameter will be extracted from this scope
+ /// @param name name of the parameter
+ /// @return a NameChangeFormat value
+ dhcp_ddns::NameChangeFormat
+ getFormat(isc::data::ConstElementPtr scope, const std::string& name);
+
+ /// @brief Returns a value converted to ReplaceClientNameMode
+ ///
+ /// Instantiation of getAndConvert() to ReplaceClientNameMode
+ ///
+ /// @param scope specified parameter will be extracted from this scope
+ /// @param name name of the parameter
+ /// @return a NameChangeFormat value
+ D2ClientConfig::ReplaceClientNameMode
+ getMode(isc::data::ConstElementPtr scope, const std::string& name);
+};
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
+
+#endif // DHCP_PARSERS_H
diff --git a/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.cc b/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.cc
new file mode 100644
index 0000000..e3bca0f
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.cc
@@ -0,0 +1,61 @@
+// Copyright (C) 2015-2023 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/dhcp_queue_control_parser.h>
+#include <util/multi_threading_mgr.h>
+#include <boost/foreach.hpp>
+#include <string>
+#include <sys/types.h>
+
+using namespace isc::data;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+ElementPtr
+DHCPQueueControlParser::parse(const ConstElementPtr& control_elem,
+ bool multi_threading_enabled) {
+ // All we really do here is verify that it is a map that
+ // contains at least queue-type. All other content depends
+ // on the packet queue implementation of that type.
+ if (control_elem->getType() != Element::map) {
+ isc_throw(DhcpConfigError, "dhcp-queue-control must be a map");
+ }
+
+ // enable-queue is mandatory.
+ bool enable_queue = getBoolean(control_elem, "enable-queue");
+
+ if (enable_queue) {
+ ConstElementPtr elem = control_elem->get("queue-type");
+ if (!elem) {
+ isc_throw(DhcpConfigError, "when queue is enabled, queue-type is required");
+ } else {
+ if (elem->getType() != Element::string) {
+ isc_throw(DhcpConfigError, "queue-type must be a string");
+ }
+ }
+ }
+
+ // Return a copy of it.
+ ElementPtr result = data::copy(control_elem);
+
+ // Currently not compatible with multi-threading.
+ if (multi_threading_enabled) {
+ // Silently disable it.
+ result->set("enable-queue", Element::create(false));
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_MT_DISABLED_QUEUE_CONTROL);
+ }
+
+ return (result);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.h b/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.h
new file mode 100644
index 0000000..d732b2e
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2018-2023 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 DHCP_QUEUE_CONTROL_PARSER_H
+#define DHCP_QUEUE_CONTROL_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.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 "dhcp-queue-control" parameter which holds the
+/// the configurable parameters that tailor DHCP packet queue behavior.
+/// In order to provide wide latitude to packet queue implementators,
+/// 'dhcp-queue-control' is mostly treated as a map of arbitrary values.
+/// There is only mandatory value, 'enable-queue', which enables/disables
+/// DHCP packet queueing. If this value is true, then the content must
+/// also include a value for 'queue-type'. Beyond these values, the
+/// map may contain any combination of valid JSON elements.
+///
+/// Unlike most other parsers, this parser primarily serves to validate
+/// the aforementioned rules, and rather than instantiate an object as
+/// a result, it simply returns a copy original map of elements.
+///
+/// This parser is used in both DHCPv4 and DHCPv6. Derived parsers
+/// are not needed.
+class DHCPQueueControlParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Constructor.
+ DHCPQueueControlParser(){};
+
+ /// @brief Parses content of the "dhcp-queue-control".
+ ///
+ /// @param control_elem MapElement containing the queue control values to
+ /// parse.
+ /// @param multi_threading_enabled The flag which indicates if MT is enabled.
+ ///
+ /// @return A copy of the of the input MapElement.
+ ///
+ /// @throw DhcpConfigError if any of the values are invalid.
+ data::ElementPtr parse(const isc::data::ConstElementPtr& control_elem,
+ bool multi_threading_enabled);
+
+private:
+};
+
+}
+} // end of namespace isc
+
+#endif // DHCP_QUEUE_CONTROL_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/duid_config_parser.cc b/src/lib/dhcpsrv/parsers/duid_config_parser.cc
new file mode 100644
index 0000000..937a542
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/duid_config_parser.cc
@@ -0,0 +1,95 @@
+// Copyright (C) 2015-2023 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 <dhcp/duid.h>
+#include <dhcpsrv/cfg_duid.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/parsers/duid_config_parser.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <exceptions/exceptions.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+void
+DUIDConfigParser::parse(const CfgDUIDPtr& cfg,
+ isc::data::ConstElementPtr duid_configuration) {
+ if (!cfg) {
+ // Sanity check
+ isc_throw(DhcpConfigError, "Must provide valid pointer to cfg when parsing duid");
+ }
+
+ std::string param;
+ try {
+ param = "type";
+ std::string duid_type = getString(duid_configuration, "type");
+ // Map DUID type represented as text into numeric value.
+ DUID::DUIDType numeric_type = DUID::DUID_UNKNOWN;
+ if (duid_type == "LLT") {
+ numeric_type = DUID::DUID_LLT;
+ } else if (duid_type == "EN") {
+ numeric_type = DUID::DUID_EN;
+ } else if (duid_type == "LL") {
+ numeric_type = DUID::DUID_LL;
+ } else {
+ isc_throw(BadValue, "unsupported DUID type '"
+ << duid_type << "'. Expected: LLT, EN or LL");
+ }
+
+ cfg->setType(static_cast<DUID::DUIDType>(numeric_type));
+
+ param = "identifier";
+ if (duid_configuration->contains(param)) {
+ cfg->setIdentifier(getString(duid_configuration, param));
+ }
+
+ param = "htype";
+ if (duid_configuration->contains(param)) {
+ cfg->setHType(getUint16(duid_configuration, param));
+ }
+
+ param = "time";
+ if (duid_configuration->contains(param)) {
+ cfg->setTime(getUint32(duid_configuration, param));
+ }
+
+ param = "enterprise-id";
+ if (duid_configuration->contains(param)) {
+ cfg->setEnterpriseId(getUint32(duid_configuration, param));
+ }
+
+ param = "persist";
+ if (duid_configuration->contains(param)) {
+ cfg->setPersist(getBoolean(duid_configuration, param));
+ }
+
+ param = "user-context";
+ ConstElementPtr user_context = duid_configuration->get("user-context");
+ if (user_context) {
+ cfg->setContext(user_context);
+ }
+ } catch (const DhcpConfigError&) {
+ throw;
+ } catch (const std::exception& ex) {
+ // Append position.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << getPosition(param, duid_configuration) << ")");
+ }
+
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_CONFIGURE_SERVERID);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/duid_config_parser.h b/src/lib/dhcpsrv/parsers/duid_config_parser.h
new file mode 100644
index 0000000..8780031
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/duid_config_parser.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2015,2017 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 DUID_CONFIG_PARSER_H
+#define DUID_CONFIG_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for server DUID configuration.
+///
+/// This parser currently supports the following DUID types:
+/// - DUID-LLT,
+/// - DUID-EN
+/// - DUID-LL
+///
+/// @todo Add support for DUID-UUID in the parser.
+class DUIDConfigParser : public isc::data::SimpleParser {
+public:
+ /// @brief Parses DUID configuration.
+ ///
+ /// @param cfg parsed DUID configuration will be stored here
+ /// @param duid_configuration Data element holding a map representing
+ /// DUID configuration.
+ ///
+ /// @throw DhcpConfigError If the configuration is invalid.
+ void parse(const CfgDUIDPtr& cfg, isc::data::ConstElementPtr duid_configuration);
+};
+
+}
+} // end of namespace isc
+
+#endif // DUID_CONFIG_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/expiration_config_parser.cc b/src/lib/dhcpsrv/parsers/expiration_config_parser.cc
new file mode 100644
index 0000000..3bbe333
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/expiration_config_parser.cc
@@ -0,0 +1,69 @@
+// Copyright (C) 2015-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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/data.h>
+#include <dhcpsrv/cfg_expiration.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/expiration_config_parser.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <boost/foreach.hpp>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+void
+ExpirationConfigParser::parse(ConstElementPtr expiration_config) {
+ CfgExpirationPtr cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration();
+
+ std::string param;
+
+ try {
+ param = "reclaim-timer-wait-time";
+ if (expiration_config->contains(param)) {
+ cfg->setReclaimTimerWaitTime(getInteger(expiration_config, param));
+ }
+
+ param = "flush-reclaimed-timer-wait-time";
+ if (expiration_config->contains(param)) {
+ cfg->setFlushReclaimedTimerWaitTime(getInteger(expiration_config,
+ param));
+ }
+
+ param = "hold-reclaimed-time";
+ if (expiration_config->contains(param)) {
+ cfg->setHoldReclaimedTime(getInteger(expiration_config, param));
+ }
+
+ param = "max-reclaim-leases";
+ if (expiration_config->contains(param)) {
+ cfg->setMaxReclaimLeases(getInteger(expiration_config, param));
+ }
+
+ param = "max-reclaim-time";
+ if (expiration_config->contains(param)) {
+ cfg->setMaxReclaimTime(getInteger(expiration_config, param));
+ }
+
+ param = "unwarned-reclaim-cycles";
+ if (expiration_config->contains(param)) {
+ cfg->setUnwarnedReclaimCycles(
+ getInteger(expiration_config, param));
+ }
+ } catch (const DhcpConfigError&) {
+ throw;
+ } catch (const std::exception& ex) {
+ // Append position of the configuration parameter to the error message.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << getPosition(param, expiration_config) << ")");
+ }
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/expiration_config_parser.h b/src/lib/dhcpsrv/parsers/expiration_config_parser.h
new file mode 100644
index 0000000..44ba77d
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/expiration_config_parser.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2015,2017 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 EXPIRATION_CONFIG_PARSER_H
+#define EXPIRATION_CONFIG_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+
+namespace isc {
+namespace dhcp {
+
+
+/// @brief Parser for the configuration parameters pertaining to the
+/// processing of expired leases.
+///
+/// This parser iterates over parameters stored in the map and tries to
+/// set the appropriate values in the @c CfgExpiration object of the
+/// Configuration Manager.
+///
+/// Currently supported parameters are:
+/// - reclaim-timer-wait-time,
+/// - flush-reclaimed-timer-wait-time,
+/// - hold-reclaimed-time,
+/// - max-reclaim-leases,
+/// - max-reclaim-time,
+/// - unwarned-reclaim-cycles.
+///
+/// These parameters are optional and the default values are used for
+/// those that aren't specified.
+///
+/// The parser checks if the values of the specified parameters are within
+/// the allowed ranges and throws exception if they aren't. Each parameter
+/// has a corresponding maximum value defined in the @c CfgExpiration class.
+/// None of them may be negative.
+class ExpirationConfigParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Destructor.
+ virtual ~ExpirationConfigParser() { }
+
+ /// @brief Parses parameters in the JSON map, pertaining to the processing
+ /// of the expired leases.
+ ///
+ /// @param expiration_config pointer to the content of parsed values
+ ///
+ /// @throw DhcpConfigError if unknown parameter specified or the
+ /// parameter contains invalid value..
+ void parse(isc::data::ConstElementPtr expiration_config);
+
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // EXPIRATION_CONFIG_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/host_reservation_parser.cc b/src/lib/dhcpsrv/parsers/host_reservation_parser.cc
new file mode 100644
index 0000000..751ca09
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/host_reservation_parser.cc
@@ -0,0 +1,457 @@
+// Copyright (C) 2014-2023 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 <asiolink/io_address.h>
+#include <asiolink/addr_utilities.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/host_reservation_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <algorithm>
+#include <sys/socket.h>
+#include <sstream>
+#include <string>
+
+using namespace isc::asiolink;
+using namespace isc::data;
+
+namespace {
+
+/// @brief Returns set of the supported parameters for DHCPv4.
+///
+/// This function returns the set of supported parameters for
+/// host reservation in DHCPv4.
+///
+/// @param identifiers_only Indicates if the function should only
+/// return supported host identifiers (if true) or all supported
+/// parameters (if false).
+const std::set<std::string>&
+getSupportedParams4(const bool identifiers_only = false) {
+ // Holds set of host identifiers.
+ static std::set<std::string> identifiers_set;
+ // Holds set of all supported parameters, including identifiers.
+ static std::set<std::string> params_set;
+ // If this is first execution of this function, we need
+ // to initialize the set.
+ if (identifiers_set.empty()) {
+ identifiers_set.insert("hw-address");
+ identifiers_set.insert("duid");
+ identifiers_set.insert("circuit-id");
+ identifiers_set.insert("client-id");
+ identifiers_set.insert("flex-id");
+ }
+ // Copy identifiers and add all other parameters.
+ if (params_set.empty()) {
+ params_set = identifiers_set;
+ params_set.insert("hostname");
+ params_set.insert("ip-address");
+ params_set.insert("option-data");
+ params_set.insert("next-server");
+ params_set.insert("server-hostname");
+ params_set.insert("boot-file-name");
+ params_set.insert("client-classes");
+ params_set.insert("user-context");
+ }
+ return (identifiers_only ? identifiers_set : params_set);
+}
+
+/// @brief Returns set of the supported parameters for DHCPv6.
+///
+/// This function returns the set of supported parameters for
+/// host reservation in DHCPv6.
+///
+/// @param identifiers_only Indicates if the function should only
+/// return supported host identifiers (if true) or all supported
+/// parameters (if false).
+const std::set<std::string>&
+getSupportedParams6(const bool identifiers_only = false) {
+ // Holds set of host identifiers.
+ static std::set<std::string> identifiers_set;
+ // Holds set of all supported parameters, including identifiers.
+ static std::set<std::string> params_set;
+ // If this is first execution of this function, we need
+ // to initialize the set.
+ if (identifiers_set.empty()) {
+ identifiers_set.insert("hw-address");
+ identifiers_set.insert("duid");
+ identifiers_set.insert("flex-id");
+ }
+ // Copy identifiers and add all other parameters.
+ if (params_set.empty()) {
+ params_set = identifiers_set;
+ params_set.insert("hostname");
+ params_set.insert("ip-addresses");
+ params_set.insert("prefixes");
+ params_set.insert("option-data");
+ params_set.insert("client-classes");
+ params_set.insert("user-context");
+ }
+ return (identifiers_only ? identifiers_set : params_set);
+}
+
+}
+
+namespace isc {
+namespace dhcp {
+
+HostPtr
+HostReservationParser::parse(const SubnetID& subnet_id,
+ isc::data::ConstElementPtr reservation_data,
+ bool encapsulate_options) {
+ return (parseInternal(subnet_id, reservation_data, encapsulate_options));
+}
+
+HostPtr
+HostReservationParser::parseInternal(const SubnetID&,
+ isc::data::ConstElementPtr reservation_data,
+ bool) {
+ std::string identifier;
+ std::string identifier_name;
+ std::string hostname;
+ ConstElementPtr user_context;
+ HostPtr host;
+
+ try {
+ // Gather those parameters that are common for both IPv4 and IPv6
+ // reservations.
+ BOOST_FOREACH(auto element, reservation_data->mapValue()) {
+ // Check if we support this parameter.
+ if (!isSupportedParameter(element.first)) {
+ isc_throw(DhcpConfigError, "unsupported configuration"
+ " parameter '" << element.first << "'");
+ }
+
+ if (isIdentifierParameter(element.first)) {
+ if (!identifier.empty()) {
+ isc_throw(DhcpConfigError, "the '" << element.first
+ << "' and '" << identifier_name
+ << "' are mutually exclusive");
+ }
+ identifier = element.second->stringValue();
+ identifier_name = element.first;
+
+ } else if (element.first == "hostname") {
+ hostname = element.second->stringValue();
+ } else if (element.first == "user-context") {
+ user_context = element.second;
+ }
+ }
+
+ // Host identifier is a must.
+ if (identifier_name.empty()) {
+ // If there is no identifier specified, we have to display an
+ // error message and include the information what identifiers
+ // are supported.
+ std::ostringstream s;
+ BOOST_FOREACH(std::string param_name, getSupportedParameters(true)) {
+ if (s.tellp() != std::streampos(0)) {
+ s << ", ";
+ }
+ s << param_name;
+ }
+ isc_throw(DhcpConfigError, "one of the supported identifiers must"
+ " be specified for host reservation: "
+ << s.str());
+
+ }
+
+ // Create a host object from the basic parameters we already parsed.
+ host.reset(new Host(identifier, identifier_name, SUBNET_ID_UNUSED,
+ SUBNET_ID_UNUSED, IOAddress("0.0.0.0"), hostname));
+
+ // Add user context
+ if (user_context) {
+ host->setContext(user_context);
+ }
+ } catch (const std::exception& ex) {
+ // Append line number where the error occurred.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << reservation_data->getPosition() << ")");
+ }
+
+ return (host);
+}
+
+bool
+HostReservationParser::isIdentifierParameter(const std::string& param_name) const {
+ return (getSupportedParameters(true).count(param_name) > 0);
+}
+
+bool
+HostReservationParser::isSupportedParameter(const std::string& param_name) const {
+ return (getSupportedParameters(false).count(param_name) > 0);
+}
+
+HostPtr
+HostReservationParser4::parseInternal(const SubnetID& subnet_id,
+ isc::data::ConstElementPtr reservation_data,
+ bool encapsulate_options) {
+ HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data,
+ encapsulate_options);
+
+ host->setIPv4SubnetID(subnet_id);
+
+ BOOST_FOREACH(auto element, reservation_data->mapValue()) {
+ // For 'option-data' element we will use another parser which
+ // already returns errors with position appended, so don't
+ // surround it with try-catch.
+ if (element.first == "option-data") {
+ CfgOptionPtr cfg_option = host->getCfgOption4();
+
+ // This parser is converted to SimpleParser already. It
+ // parses the Element structure immediately, there's no need
+ // to go through build/commit phases.
+ OptionDataListParser parser(AF_INET);
+ parser.parse(cfg_option, element.second, encapsulate_options);
+
+ // Everything else should be surrounded with try-catch to append
+ // position.
+ } else {
+ try {
+ if (element.first == "ip-address") {
+ host->setIPv4Reservation(IOAddress(element.second->
+ stringValue()));
+ } else if (element.first == "next-server") {
+ host->setNextServer(IOAddress(element.second->stringValue()));
+
+ } else if (element.first == "server-hostname") {
+ host->setServerHostname(element.second->stringValue());
+
+ } else if (element.first == "boot-file-name") {
+ host->setBootFileName(element.second->stringValue());
+
+ } else if (element.first == "client-classes") {
+ BOOST_FOREACH(ConstElementPtr class_element,
+ element.second->listValue()) {
+ host->addClientClass4(class_element->stringValue());
+ }
+ }
+
+ } catch (const std::exception& ex) {
+ // Append line number where the error occurred.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << element.second->getPosition() << ")");
+ }
+ }
+ }
+
+ return (host);
+}
+
+const std::set<std::string>&
+HostReservationParser4::getSupportedParameters(const bool identifiers_only) const {
+ return (getSupportedParams4(identifiers_only));
+}
+
+HostPtr
+HostReservationParser6::parseInternal(const SubnetID& subnet_id,
+ isc::data::ConstElementPtr reservation_data,
+ bool encapsulate_options) {
+ HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data,
+ encapsulate_options);
+
+ host->setIPv6SubnetID(subnet_id);
+
+ BOOST_FOREACH(auto element, reservation_data->mapValue()) {
+ // Parse option values. Note that the configuration option parser
+ // returns errors with position information appended, so there is no
+ // need to surround it with try-clause (and rethrow with position
+ // appended).
+ if (element.first == "option-data") {
+ CfgOptionPtr cfg_option = host->getCfgOption6();
+
+ // This parser is converted to SimpleParser already. It
+ // parses the Element structure immediately, there's no need
+ // to go through build/commit phases.
+ OptionDataListParser parser(AF_INET6);
+ parser.parse(cfg_option, element.second, encapsulate_options);
+
+ } else if (element.first == "ip-addresses" || element.first == "prefixes") {
+ BOOST_FOREACH(ConstElementPtr prefix_element,
+ element.second->listValue()) {
+ try {
+ // For the IPv6 address the prefix length is 128 and the
+ // value specified in the list is a reserved address.
+ IPv6Resrv::Type resrv_type = IPv6Resrv::TYPE_NA;
+ std::string prefix = prefix_element->stringValue();
+ uint8_t prefix_len = 128;
+
+ // If we're dealing with prefixes, instead of addresses,
+ // we will have to extract the prefix length from the value
+ // specified in the following format: 2001:db8:2000::/64.
+ if (element.first == "prefixes") {
+ // The slash is mandatory for prefixes. If there is no
+ // slash, return an error.
+ size_t len_pos = prefix.find('/');
+ if (len_pos == std::string::npos) {
+ isc_throw(DhcpConfigError, "prefix reservation"
+ " requires prefix length be specified"
+ " in '" << prefix << "'");
+
+ // If there is nothing after the slash, we should also
+ // report an error.
+ } else if (len_pos >= prefix.length() - 1) {
+ isc_throw(DhcpConfigError, "prefix '" << prefix
+ << "' requires length after '/'");
+
+ }
+
+ // Convert the prefix length from the string to the
+ // number. Note, that we don't use the uint8_t type
+ // as the lexical cast would expect a character, e.g.
+ // 'a', instead of prefix length, e.g. '64'.
+ try {
+ prefix_len = boost::lexical_cast<unsigned int>(prefix.substr(len_pos + 1));
+
+ } catch (const boost::bad_lexical_cast&) {
+ isc_throw(DhcpConfigError, "prefix length value '"
+ << prefix.substr(len_pos + 1)
+ << "' is invalid");
+ }
+
+ if ((prefix_len == 0) || (prefix_len > 128)) {
+ isc_throw(OutOfRange,
+ "'prefix-len' value must be in range of [1..128]");
+ }
+
+ // Remove the slash character and the prefix length
+ // from the parsed value.
+ prefix.erase(len_pos);
+
+ // Finally, set the reservation type.
+ resrv_type = IPv6Resrv::TYPE_PD;
+
+ if (prefix_len != 128) {
+ IOAddress addr(prefix);
+ IOAddress first_address = firstAddrInPrefix(addr, prefix_len);
+ if (first_address != addr) {
+ isc_throw(BadValue, "Prefix address: " << addr
+ << " exceeds prefix/prefix-len pair: " << first_address
+ << "/" << static_cast<uint32_t>(prefix_len));
+ }
+ }
+ }
+
+ // Create a reservation for an address or prefix.
+ host->addReservation(IPv6Resrv(resrv_type,
+ IOAddress(prefix),
+ prefix_len));
+
+ } catch (const std::exception& ex) {
+ // Append line number where the error occurred.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << prefix_element->getPosition() << ")");
+ }
+ }
+
+ } else if (element.first == "client-classes") {
+ try {
+ BOOST_FOREACH(ConstElementPtr class_element,
+ element.second->listValue()) {
+ host->addClientClass6(class_element->stringValue());
+ }
+ } catch (const std::exception& ex) {
+ // Append line number where the error occurred.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << element.second->getPosition() << ")");
+ }
+ }
+ }
+
+ return (host);
+}
+
+const std::set<std::string>&
+HostReservationParser6::getSupportedParameters(const bool identifiers_only) const {
+ return (getSupportedParams6(identifiers_only));
+}
+
+HostReservationIdsParser::HostReservationIdsParser()
+ : staging_cfg_() {
+}
+
+void
+HostReservationIdsParser::parse(isc::data::ConstElementPtr ids_list) {
+ parseInternal(ids_list);
+}
+
+void
+HostReservationIdsParser::parseInternal(isc::data::ConstElementPtr ids_list) {
+ // Remove existing identifier types.
+ staging_cfg_->clearIdentifierTypes();
+
+ BOOST_FOREACH(ConstElementPtr element, ids_list->listValue()) {
+ std::string id_name = element->stringValue();
+ try {
+ if (id_name != "auto") {
+ if (!isSupportedIdentifier(id_name)) {
+ isc_throw(isc::BadValue, "unsupported identifier '"
+ << id_name << "'");
+ }
+ staging_cfg_->addIdentifierType(id_name);
+
+ } else {
+ // 'auto' is mutually exclusive with other values. If there
+ // are any values in the configuration already it means that
+ // some other values have already been specified.
+ if (!staging_cfg_->getIdentifierTypes().empty()) {
+ isc_throw(isc::BadValue, "if 'auto' keyword is used,"
+ " no other values can be specified within '"
+ "host-reservation-identifiers' list");
+ }
+ // Iterate over all identifier types and for those supported
+ // in a given context (DHCPv4 or DHCPv6) add the identifier type
+ // to the configuration.
+ for (unsigned int i = 0;
+ i <= static_cast<unsigned int>(Host::LAST_IDENTIFIER_TYPE);
+ ++i) {
+ std::string supported_id_name =
+ Host::getIdentifierName(static_cast<Host::IdentifierType>(i));
+ if (isSupportedIdentifier(supported_id_name)) {
+ staging_cfg_->addIdentifierType(supported_id_name);
+ }
+ }
+ }
+
+ } catch (const std::exception& ex) {
+ // Append line number where the error occurred.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << element->getPosition() << ")");
+ }
+ }
+
+ // The parsed list must not be empty.
+ if (staging_cfg_->getIdentifierTypes().empty()) {
+ isc_throw(DhcpConfigError, "'host-reservation-identifiers' list must not"
+ " be empty (" << ids_list->getPosition() << ")");
+ }
+
+}
+
+HostReservationIdsParser4::HostReservationIdsParser4()
+ : HostReservationIdsParser() {
+ staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostOperations4();
+}
+
+bool
+HostReservationIdsParser4::isSupportedIdentifier(const std::string& id_name) const {
+ return (getSupportedParams4(true).count(id_name) > 0);
+}
+
+HostReservationIdsParser6::HostReservationIdsParser6()
+ : HostReservationIdsParser() {
+ staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostOperations6();
+}
+
+bool
+HostReservationIdsParser6::isSupportedIdentifier(const std::string& id_name) const {
+ return (getSupportedParams6(true).count(id_name) > 0);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/host_reservation_parser.h b/src/lib/dhcpsrv/parsers/host_reservation_parser.h
new file mode 100644
index 0000000..2c2f260
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/host_reservation_parser.h
@@ -0,0 +1,243 @@
+// Copyright (C) 2014-2023 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 HOST_RESERVATION_PARSER_H
+#define HOST_RESERVATION_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/host.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for a single host reservation entry.
+class HostReservationParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Destructor.
+ virtual ~HostReservationParser() { }
+
+ /// @brief Parses a single entry for host reservation.
+ ///
+ /// @param subnet_id Identifier of the subnet that the host is
+ /// connected to.
+ /// @param reservation_data Data element holding map with a host
+ /// reservation configuration.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ ///
+ /// @return Pointer to the object representing parsed host.
+ /// @throw DhcpConfigError If the configuration is invalid.
+ virtual HostPtr
+ parse(const SubnetID& subnet_id,
+ isc::data::ConstElementPtr reservation_data,
+ bool encapsulate_options = true) final;
+
+protected:
+
+ /// @brief Parses a single entry for host reservation.
+ ///
+ /// This method is called by @ref parse and it can be overridden in the
+ /// derived classes to provide class specific parsing logic.
+ ///
+ /// @param subnet_id Identifier of the subnet that the host is
+ /// connected to.
+ /// @param reservation_data Data element holding map with a host
+ /// reservation configuration.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ ///
+ /// @return Pointer to the object representing parsed host.
+ /// @throw DhcpConfigError If the configuration is invalid.
+ virtual HostPtr parseInternal(const SubnetID& subnet_id,
+ isc::data::ConstElementPtr reservation_data,
+ bool encapsulate_options);
+
+ /// @brief Checks if the specified parameter is a host identifier.
+ ///
+ /// @param param_name Parameter name.
+ ///
+ /// @return true if the parameter specifies host identifier, false
+ /// otherwise.
+ virtual bool isIdentifierParameter(const std::string& param_name) const;
+
+ /// @brief Checks if the specified parameter is supported by the parser.
+ ///
+ /// @param param_name Parameter name.
+ ///
+ /// @return true if the parameter is supported, false otherwise.
+ virtual bool isSupportedParameter(const std::string& param_name) const;
+
+ /// @brief Returns set of the supported parameters.
+ ///
+ /// @param identifiers_only Indicates if the function should only
+ /// return supported host identifiers (if true) or all supported
+ /// parameters (if false).
+ ///
+ /// @return Set of supported parameter names.
+ virtual const std::set<std::string>&
+ getSupportedParameters(const bool identifiers_only) const = 0;
+};
+
+/// @brief Parser for a single host reservation for DHCPv4.
+class HostReservationParser4 : public HostReservationParser {
+protected:
+
+ /// @brief Parses a single host reservation for DHCPv4.
+ ///
+ /// @param subnet_id Identifier of the subnet that the host is
+ /// connected to.
+ /// @param reservation_data Data element holding map with a host
+ /// reservation configuration.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ ///
+ /// @return Pointer to the object representing parsed host.
+ /// @throw DhcpConfigError If the configuration is invalid.
+ virtual HostPtr parseInternal(const SubnetID& subnet_id,
+ isc::data::ConstElementPtr reservation_data,
+ bool encapsulate_options);
+
+ /// @brief Returns set of the supported parameters for DHCPv4.
+ ///
+ /// @param identifiers_only Indicates if the function should only
+ /// return supported host identifiers (if true) or all supported
+ /// parameters (if false).
+ ///
+ /// @return Set of supported parameter names.
+ virtual const std::set<std::string>&
+ getSupportedParameters(const bool identifiers_only) const;
+};
+
+/// @brief Parser for a single host reservation for DHCPv6.
+class HostReservationParser6 : public HostReservationParser {
+protected:
+
+ /// @brief Parses a single host reservation for DHCPv6.
+ ///
+ /// @param subnet_id Identifier of the subnet that the host is
+ /// connected to.
+ /// @param reservation_data Data element holding map with a host
+ /// reservation configuration.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ ///
+ /// @return Pointer to the object representing parsed host.
+ /// @throw DhcpConfigError If the configuration is invalid.
+ virtual HostPtr parseInternal(const SubnetID& subnet_id,
+ isc::data::ConstElementPtr reservation_data,
+ bool encapsulate_options);
+
+ /// @brief Returns set of the supported parameters for DHCPv6.
+ ///
+ /// @param identifiers_only Indicates if the function should only
+ /// return supported host identifiers (if true) or all supported
+ /// parameters (if false).
+ ///
+ /// @return Set of supported parameter names.
+ virtual const std::set<std::string>&
+ getSupportedParameters(const bool identifiers_only) const;
+
+};
+
+/// @brief Parser for a list of host identifiers.
+///
+/// This is a parent parser class for parsing "host-reservation-identifiers"
+/// global configuration parameter. The DHCPv4 and DHCPv6 specific parsers
+/// derive from this class.
+class HostReservationIdsParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Constructor.
+ HostReservationIdsParser();
+
+ /// @brief Destructor.
+ virtual ~HostReservationIdsParser() { }
+
+ /// @brief Parses a list of host identifiers.
+ ///
+ /// @param ids_list Data element pointing to an ordered list of host
+ /// identifier names.
+ ///
+ /// @throw DhcpConfigError If specified configuration is invalid.
+ void parse(isc::data::ConstElementPtr ids_list);
+
+protected:
+
+ /// @brief Parses a list of host identifiers.
+ ///
+ /// This method is called by @ref parse and it can be overridden in the
+ /// derived classes to provide class specific parsing logic.
+ ///
+ /// @param ids_list Data element pointing to an ordered list of host
+ /// identifier names.
+ ///
+ /// @throw DhcpConfigError If specified configuration is invalid.
+ virtual void parseInternal(isc::data::ConstElementPtr ids_list);
+
+ /// @brief Checks if specified identifier name is supported in the
+ /// context of the parser.
+ ///
+ /// This is abstract method which must be implemented in the derived
+ /// parser classes for DHCPv4 and DHCPv6.
+ ///
+ /// @param id_name Identifier name.
+ /// @return true if the specified identifier is supported, false
+ /// otherwise.
+ virtual bool isSupportedIdentifier(const std::string& id_name) const = 0;
+
+ /// @brief Pointer to the object holding configuration.
+ CfgHostOperationsPtr staging_cfg_;
+
+};
+
+/// @brief Parser for a list of host identifiers for DHCPv4.
+class HostReservationIdsParser4 : public HostReservationIdsParser {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Initializes staging configuration pointer to the one used for DHCPv4
+ /// configuration.
+ HostReservationIdsParser4();
+
+protected:
+
+ /// @brief Checks if specified identifier name is supported for DHCPv4.
+ ///
+ /// @param id_name Identifier name.
+ /// @return true if the specified identifier is supported, false
+ /// otherwise.
+ virtual bool isSupportedIdentifier(const std::string& id_name) const;
+
+};
+
+/// @brief Parser for a list of host identifiers for DHCPv6.
+class HostReservationIdsParser6 : public HostReservationIdsParser {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Initializes staging configuration pointer to the one used for DHCPv6
+ /// configuration.
+ HostReservationIdsParser6();
+
+protected:
+
+ /// @brief Checks if specified identifier name is supported for DHCPv6.
+ ///
+ /// @param id_name Identifier name.
+ /// @return true if the specified identifier is supported, false
+ /// otherwise.
+ virtual bool isSupportedIdentifier(const std::string& id_name) const;
+};
+
+
+}
+} // end of namespace isc
+
+#endif // HOST_RESERVATION_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/host_reservations_list_parser.h b/src/lib/dhcpsrv/parsers/host_reservations_list_parser.h
new file mode 100644
index 0000000..9f6ce2f
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/host_reservations_list_parser.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2014-2017 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 HOST_RESERVATIONS_LIST_PARSER_H
+#define HOST_RESERVATIONS_LIST_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/host.h>
+#include <dhcpsrv/subnet_id.h>
+#include <boost/foreach.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for a list of host reservations for a subnet.
+///
+/// @tparam HostReservationParserType Host reservation parser to be used to
+/// parse individual reservations: @c HostReservationParser4 or
+/// @c HostReservationParser6.
+template<typename HostReservationParserType>
+class HostReservationsListParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Parses a list of host reservation entries for a subnet.
+ ///
+ /// @param subnet_id Identifier of the subnet to which the reservations
+ /// belong.
+ /// @param hr_list Data element holding a list of host reservations.
+ /// Each host reservation is described by a map object.
+ /// @param [out] hosts_list Hosts representing parsed reservations are stored
+ /// in this list.
+ ///
+ /// @throw DhcpConfigError If the configuration if any of the reservations
+ /// is invalid.
+ void parse(const SubnetID& subnet_id, isc::data::ConstElementPtr hr_list,
+ HostCollection& hosts_list) {
+ HostCollection hosts;
+ BOOST_FOREACH(data::ConstElementPtr reservation, hr_list->listValue()) {
+ HostReservationParserType parser;
+ hosts.push_back(parser.parse(subnet_id, reservation));
+ }
+ hosts_list.swap(hosts);
+ }
+};
+
+}
+}
+
+#endif // HOST_RESERVATIONS_LIST_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc b/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc
new file mode 100644
index 0000000..c375b40
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc
@@ -0,0 +1,130 @@
+// Copyright (C) 2015-2023 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/ifaces_config_parser.h>
+#include <boost/foreach.hpp>
+#include <string>
+#include <sys/types.h>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+void
+IfacesConfigParser::parseInterfacesList(const CfgIfacePtr& cfg_iface,
+ ConstElementPtr ifaces_list) {
+ BOOST_FOREACH(ConstElementPtr iface, ifaces_list->listValue()) {
+ std::string iface_name = iface->stringValue();
+ try {
+ cfg_iface->use(protocol_, iface_name);
+
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, "Failed to select interface: "
+ << ex.what() << " (" << iface->getPosition() << ")");
+ }
+ }
+}
+
+IfacesConfigParser::IfacesConfigParser(const uint16_t protocol, bool test_mode)
+ : protocol_(protocol), test_mode_(test_mode) {
+}
+
+void
+IfacesConfigParser::parse(const CfgIfacePtr& cfg,
+ const isc::data::ConstElementPtr& ifaces_config) {
+
+ // Check for re-detect before calling parseInterfacesList()
+ bool re_detect = getBoolean(ifaces_config, "re-detect");
+ cfg->setReDetect(re_detect);
+ if (re_detect && !test_mode_) {
+ IfaceMgr::instance().clearIfaces();
+ IfaceMgr::instance().detectIfaces();
+ }
+
+ bool socket_type_specified = false;
+ BOOST_FOREACH(ConfigPair element, ifaces_config->mapValue()) {
+ try {
+ if (element.first == "re-detect") {
+ continue;
+ }
+
+ if (element.first == "interfaces") {
+ parseInterfacesList(cfg, element.second);
+ continue;
+ }
+
+ if (element.first == "dhcp-socket-type") {
+ if (protocol_ == AF_INET) {
+ cfg->useSocketType(AF_INET, element.second->stringValue());
+ socket_type_specified = true;
+ continue;
+ } else {
+ isc_throw(DhcpConfigError,
+ "dhcp-socket-type is not supported in DHCPv6");
+ }
+ }
+
+ if (element.first == "outbound-interface") {
+ if (protocol_ == AF_INET) {
+ CfgIface::OutboundIface type =
+ CfgIface::textToOutboundIface(element.second->stringValue());
+ cfg->setOutboundIface(type);
+ continue;
+ } else {
+ isc_throw(DhcpConfigError,
+ "outbound-interface is not supported in DHCPv6");
+ }
+ }
+
+ if (element.first == "service-sockets-require-all") {
+ cfg->setServiceSocketsRequireAll(element.second->boolValue());
+ continue;
+ }
+
+ if (element.first == "service-sockets-retry-wait-time") {
+ cfg->setServiceSocketsRetryWaitTime(static_cast<uint32_t>(element.second->intValue()));
+ continue;
+ }
+
+ if (element.first == "service-sockets-max-retries") {
+ cfg->setServiceSocketsMaxRetries(static_cast<uint32_t>(element.second->intValue()));
+ continue;
+ }
+
+ if (element.first == "user-context") {
+ cfg->setContext(element.second);
+ continue;
+ }
+
+ // This should never happen as the input produced by the parser
+ // see (src/bin/dhcpX/dhcpX_parser.yy) should not produce any
+ // other parameter, so this case is only to catch bugs in
+ // the parser.
+ isc_throw(DhcpConfigError, "unsupported parameter '"
+ << element.first << "'");
+ } catch (const std::exception& ex) {
+ // Append line number where the error occurred.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << element.second->getPosition() << ")");
+ }
+ }
+
+ // User hasn't specified the socket type. Log that we are using
+ // the default type. Log it only if this is DHCPv4. (DHCPv6 does not use
+ // raw sockets).
+ if (!socket_type_specified && (protocol_ == AF_INET) ) {
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_SOCKET_TYPE_DEFAULT)
+ .arg(cfg->socketTypeToText());
+ }
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/ifaces_config_parser.h b/src/lib/dhcpsrv/parsers/ifaces_config_parser.h
new file mode 100644
index 0000000..9eb25cb
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/ifaces_config_parser.h
@@ -0,0 +1,70 @@
+// 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/.
+
+#ifndef IFACES_CONFIG_PARSER_H
+#define IFACES_CONFIG_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/cfg_iface.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for the configuration of interfaces.
+///
+/// This parser parses the "interfaces-config" parameter which holds the
+/// full configuration of the DHCP server with respect to the use of
+/// interfaces, DHCP traffic sockets and alike.
+///
+/// This parser is used in both DHCPv4 and DHCPv6. Derived parsers
+/// are not needed.
+class IfacesConfigParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Constructor
+ ///
+ /// In test mode only the configuration is checked. In particular
+ /// sockets are not opened or closed.
+ ///
+ /// @param protocol AF_INET for DHCPv4 and AF_INET6 for DHCPv6.
+ /// @param test_mode True if in test mode, False if not.
+ IfacesConfigParser(const uint16_t protocol, bool test_mode);
+
+ /// @brief Parses content of the "interfaces-config".
+ ///
+ /// @param config parsed structures will be stored here
+ /// @param values pointer to the content of parsed values
+ ///
+ /// @throw DhcpConfigError if the interface names and/or addresses
+ /// are invalid.
+ void parse(const CfgIfacePtr& config, const isc::data::ConstElementPtr& values);
+
+private:
+ /// @brief parses interfaces-list structure
+ ///
+ /// This method goes through all the interfaces-specified in
+ /// 'interfaces-list' and enabled them in the specified configuration
+ /// structure
+ ///
+ /// @param cfg_iface parsed interfaces will be specified here
+ /// @param ifaces_list interfaces-list to be parsed
+ /// @throw DhcpConfigError if the interface names are invalid.
+ void parseInterfacesList(const CfgIfacePtr& cfg_iface,
+ isc::data::ConstElementPtr ifaces_list);
+
+ /// @brief AF_INET for DHCPv4 and AF_INET6 for DHCPv6.
+ int protocol_;
+
+ /// @brief Test mode.
+ bool test_mode_;
+};
+
+}
+} // end of namespace isc
+
+#endif // IFACES_CONFIG_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/multi_threading_config_parser.cc b/src/lib/dhcpsrv/parsers/multi_threading_config_parser.cc
new file mode 100644
index 0000000..68f6c41
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/multi_threading_config_parser.cc
@@ -0,0 +1,73 @@
+// Copyright (C) 2020-2023 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/srv_config.h>
+#include <dhcpsrv/parsers/multi_threading_config_parser.h>
+#include <util/multi_threading_mgr.h>
+
+#include <limits>
+
+using namespace isc::data;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+void
+MultiThreadingConfigParser::parse(SrvConfig& srv_cfg,
+ const ConstElementPtr& value) {
+ if (!value) {
+ return;
+ }
+ if (value->getType() != Element::map) {
+ isc_throw(DhcpConfigError, "multi-threading is supposed to be a map");
+ }
+
+ // enable-multi-threading is mandatory
+ getBoolean(value, "enable-multi-threading");
+
+ // thread-pool-size is not mandatory
+ if (value->get("thread-pool-size")) {
+ auto thread_pool_size = getInteger(value, "thread-pool-size");
+ uint32_t max_size = std::numeric_limits<uint16_t>::max();
+ if (thread_pool_size < 0) {
+ isc_throw(DhcpConfigError,
+ "thread pool size code must not be negative ("
+ << getPosition("thread-pool-size", value) << ")");
+ }
+ if (thread_pool_size > max_size) {
+ isc_throw(DhcpConfigError, "invalid thread pool size '"
+ << thread_pool_size << "', it must not be greater than '"
+ << max_size << "' ("
+ << getPosition("thread-pool-size", value) << ")");
+ }
+ }
+
+ // packet-queue-size is not mandatory
+ if (value->get("packet-queue-size")) {
+ auto packet_queue_size = getInteger(value, "packet-queue-size");
+ uint32_t max_size = std::numeric_limits<uint16_t>::max();
+ if (packet_queue_size < 0) {
+ isc_throw(DhcpConfigError,
+ "packet queue size code must not be negative ("
+ << getPosition("packet-queue-size", value) << ")");
+ }
+ if (packet_queue_size > max_size) {
+ isc_throw(DhcpConfigError, "invalid packet queue size '"
+ << packet_queue_size << "', it must not be greater than '"
+ << max_size << "' ("
+ << getPosition("packet-queue-size", value) << ")");
+ }
+ }
+
+ srv_cfg.setDHCPMultiThreading(value);
+}
+
+} // namespace dhcp
+} // namespace isc
diff --git a/src/lib/dhcpsrv/parsers/multi_threading_config_parser.h b/src/lib/dhcpsrv/parsers/multi_threading_config_parser.h
new file mode 100644
index 0000000..d099a05
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/multi_threading_config_parser.h
@@ -0,0 +1,34 @@
+// Copyright (C) 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/.
+
+#ifndef MULTI_THREADING_CONFIG_PARSER_H
+#define MULTI_THREADING_CONFIG_PARSER_H
+
+#include <cc/simple_parser.h>
+#include <dhcpsrv/srv_config.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Simple parser for multi-threading structure
+class MultiThreadingConfigParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief parses JSON structure.
+ ///
+ /// This function stores the 'multi-threading' settings in the server
+ /// configuration and updates the MT mode so that is can be checked when
+ /// parsing 'hooks-libraries'.
+ ///
+ /// @param srv_cfg parsed value will be stored here.
+ /// @param value a JSON map that contains multi-threading parameters.
+ void parse(SrvConfig& srv_cfg, const isc::data::ConstElementPtr& value);
+};
+
+} // namespace dhcp
+} // namespace isc
+
+#endif /* MULTI_THREADING_CONFIG_PARSER_H */
diff --git a/src/lib/dhcpsrv/parsers/option_data_parser.cc b/src/lib/dhcpsrv/parsers/option_data_parser.cc
new file mode 100644
index 0000000..6a321cd
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/option_data_parser.cc
@@ -0,0 +1,479 @@
+// Copyright (C) 2017-2023 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 <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option_definition.h>
+#include <dhcp/option_space.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
+#include <util/encode/hex.h>
+#include <util/strutil.h>
+#include <boost/foreach.hpp>
+#include <boost/make_shared.hpp>
+#include <limits>
+#include <vector>
+
+using namespace isc::data;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+// **************************** OptionDataParser *************************
+
+OptionDataParser::OptionDataParser(const uint16_t address_family,
+ CfgOptionDefPtr cfg_option_def)
+ : address_family_(address_family), cfg_option_def_(cfg_option_def) {
+}
+
+std::pair<OptionDescriptor, std::string>
+OptionDataParser::parse(isc::data::ConstElementPtr single_option) {
+
+ // Check parameters.
+ if (address_family_ == AF_INET) {
+ checkKeywords(SimpleParser4::OPTION4_PARAMETERS, single_option);
+ } else {
+ checkKeywords(SimpleParser6::OPTION6_PARAMETERS, single_option);
+ }
+
+ // Try to create the option instance.
+ std::pair<OptionDescriptor, std::string> opt = createOption(single_option);
+
+ if (!opt.first.option_) {
+ // Should never happen (@todo: update message)
+ isc_throw(isc::InvalidOperation,
+ "parser logic error: no option has been configured and"
+ " thus there is nothing to commit. Has build() been called?");
+ }
+
+ return (opt);
+}
+
+Optional<uint32_t>
+OptionDataParser::extractCode(ConstElementPtr parent) const {
+ uint32_t code;
+ try {
+ code = getInteger(parent, "code");
+
+ } catch (const std::exception&) {
+ // The code parameter was not found. Return an unspecified
+ // value.
+ return (Optional<uint32_t>());
+ }
+
+ if (address_family_ == AF_INET &&
+ code > std::numeric_limits<uint8_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << code
+ << "', it must not be greater than '"
+ << static_cast<int>(std::numeric_limits<uint8_t>::max())
+ << "' (" << getPosition("code", parent)
+ << ")");
+
+ } else if (address_family_ == AF_INET6 &&
+ code > std::numeric_limits<uint16_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << code
+ << "', it must not exceed '"
+ << std::numeric_limits<uint16_t>::max()
+ << "' (" << getPosition("code", parent)
+ << ")");
+
+ }
+
+ return (Optional<uint32_t>(code));
+}
+
+Optional<std::string>
+OptionDataParser::extractName(ConstElementPtr parent) const {
+ std::string name;
+ try {
+ name = getString(parent, "name");
+
+ } catch (...) {
+ return (Optional<std::string>());
+ }
+
+ if (name.find(" ") != std::string::npos) {
+ isc_throw(DhcpConfigError, "invalid option name '" << name
+ << "', space character is not allowed ("
+ << getPosition("name", parent) << ")");
+ }
+
+ return (Optional<std::string>(name));
+}
+
+Optional<std::string>
+OptionDataParser::extractData(ConstElementPtr parent) const {
+ std::string data;
+ try {
+ data = getString(parent, "data");
+
+ } catch (...) {
+ // The "data" parameter was not found. Return an empty value.
+ return (Optional<std::string>());
+ }
+
+ return (Optional<std::string>(data));
+}
+
+Optional<bool>
+OptionDataParser::extractCSVFormat(ConstElementPtr parent) const {
+ bool csv_format = true;
+ try {
+ csv_format = getBoolean(parent, "csv-format");
+
+ } catch (...) {
+ return (Optional<bool>());
+ }
+
+ return (Optional<bool>(csv_format));
+}
+
+std::string
+OptionDataParser::extractSpace(ConstElementPtr parent) const {
+ std::string space = address_family_ == AF_INET ?
+ DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
+ try {
+ space = getString(parent, "space");
+
+ } catch (...) {
+ return (space);
+ }
+
+ try {
+ if (!OptionSpace::validateName(space)) {
+ isc_throw(DhcpConfigError, "invalid option space name '"
+ << space << "'");
+ }
+
+ if ((space == DHCP4_OPTION_SPACE) && (address_family_ == AF_INET6)) {
+ isc_throw(DhcpConfigError, "'" << DHCP4_OPTION_SPACE
+ << "' option space name is reserved for DHCPv4 server");
+
+ } else if ((space == DHCP6_OPTION_SPACE) &&
+ (address_family_ == AF_INET)) {
+ isc_throw(DhcpConfigError, "'" << DHCP6_OPTION_SPACE
+ << "' option space name is reserved for DHCPv6 server");
+ }
+
+ } catch (const std::exception& ex) {
+ // Append position of the option space parameter.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << getPosition("space", parent) << ")");
+ }
+
+ return (space);
+}
+
+Optional<bool>
+OptionDataParser::extractPersistent(ConstElementPtr parent) const {
+ bool persist = false;
+ try {
+ persist = getBoolean(parent, "always-send");
+
+ } catch (...) {
+ return (Optional<bool>());
+ }
+
+ return (Optional<bool>(persist));
+}
+
+Optional<bool>
+OptionDataParser::extractCancelled(ConstElementPtr parent) const {
+ bool cancel = false;
+ try {
+ cancel = getBoolean(parent, "never-send");
+
+ } catch (...) {
+ return (Optional<bool>());
+ }
+
+ return (Optional<bool>(cancel));
+}
+
+OptionDefinitionPtr
+OptionDataParser::findOptionDefinition(const std::string& option_space,
+ const Optional<uint32_t>& option_code,
+ const Optional<std::string>& option_name) const {
+ OptionDefinitionPtr def;
+ if (cfg_option_def_) {
+ // Check if the definition was given in the constructor
+ if (option_code.unspecified()) {
+ def = cfg_option_def_->get(option_space, option_name);
+ } else {
+ def = cfg_option_def_->get(option_space, option_code);
+ }
+ }
+
+ if (!def) {
+ // Check if this is a standard option.
+ if (option_code.unspecified()) {
+ def = LibDHCP::getOptionDef(option_space, option_name);
+ } else {
+ def = LibDHCP::getOptionDef(option_space, option_code);
+ }
+ }
+
+ if (!def) {
+ // Check if this is a vendor-option. If it is, get vendor-specific
+ // definition.
+ uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
+ if (vendor_id) {
+ const Option::Universe u = address_family_ == AF_INET ?
+ Option::V4 : Option::V6;
+ if (option_code.unspecified()) {
+ def = LibDHCP::getVendorOptionDef(u, vendor_id, option_name);
+ } else {
+ def = LibDHCP::getVendorOptionDef(u, vendor_id, option_code);
+ }
+ }
+ }
+
+ if (!def) {
+ // Check if this is an option specified by a user. We used to
+ // check that in the staging configuration, but when the configuration
+ // changes are caused by a command the staging configuration doesn't
+ // exist. What is always available is the container holding runtime
+ // option definitions in LibDHCP. It holds option definitions from
+ // the staging configuration in case of the full reconfiguration or
+ // the definitions from the current configuration in case there is
+ // no staging configuration (after configuration commit). In other
+ // words, runtime options are always the ones that we need here.
+ if (option_code.unspecified()) {
+ def = LibDHCP::getRuntimeOptionDef(option_space, option_name);
+ } else {
+ def = LibDHCP::getRuntimeOptionDef(option_space, option_code);
+ }
+ }
+
+ if (!def) {
+ // Finish by last resort definitions.
+ if (option_code.unspecified()) {
+ def = LibDHCP::getLastResortOptionDef(option_space, option_name);
+ } else {
+ def = LibDHCP::getLastResortOptionDef(option_space, option_code);
+ }
+ }
+
+ return (def);
+}
+
+std::pair<OptionDescriptor, std::string>
+OptionDataParser::createOption(ConstElementPtr option_data) {
+ const Option::Universe universe = address_family_ == AF_INET ?
+ Option::V4 : Option::V6;
+
+ Optional<uint32_t> code_param = extractCode(option_data);
+ Optional<std::string> name_param = extractName(option_data);
+ Optional<bool> csv_format_param = extractCSVFormat(option_data);
+ Optional<bool> persist_param = extractPersistent(option_data);
+ Optional<bool> cancel_param = extractCancelled(option_data);
+ Optional<std::string> data_param = extractData(option_data);
+ std::string space_param = extractSpace(option_data);
+ ConstElementPtr user_context = option_data->get("user-context");
+
+ // Require that option code or option name is specified.
+ if (code_param.unspecified() && name_param.unspecified()) {
+ isc_throw(DhcpConfigError, "option data configuration requires one of"
+ " 'code' or 'name' parameters to be specified"
+ << " (" << option_data->getPosition() << ")");
+ }
+
+ // Try to find a corresponding option definition using option code or
+ // option name.
+ OptionDefinitionPtr def = findOptionDefinition(space_param, code_param, name_param);
+
+ // If there is no definition, the user must not explicitly enable the
+ // use of csv-format.
+ if (!def) {
+ // If explicitly requested that the CSV format is to be used,
+ // the option definition is a must.
+ if (!csv_format_param.unspecified() && csv_format_param) {
+ isc_throw(DhcpConfigError, "definition for the option '"
+ << space_param << "." << name_param
+ << "' having code '" << code_param
+ << "' does not exist ("
+ << getPosition("name", option_data)
+ << ")");
+
+ // If there is no option definition and the option code is not specified
+ // we have no means to find the option code.
+ } else if (!name_param.unspecified() && code_param.unspecified()) {
+ isc_throw(DhcpConfigError, "definition for the option '"
+ << space_param << "." << name_param
+ << "' does not exist ("
+ << getPosition("name", option_data)
+ << ")");
+ }
+ } else {
+ // Option name is specified it should match the name in the definition.
+ if (!name_param.unspecified() && (def->getName() != name_param.get())) {
+ isc_throw(DhcpConfigError, "specified option name '"
+ << name_param << "' does not match the "
+ << "option definition: '" << space_param
+ << "." << def->getName() << "' ("
+ << getPosition("name", option_data)
+ << ")");
+ }
+ }
+
+ // No data and cancelled is a supported special case.
+ if (!cancel_param.unspecified() && cancel_param &&
+ data_param.unspecified()) {
+ uint16_t code;
+ if (def) {
+ code = def->getCode();
+ } else {
+ code = static_cast<uint16_t>(code_param);
+ }
+ OptionPtr option(new Option(universe, code));
+ bool persistent = !persist_param.unspecified() && persist_param;
+ OptionDescriptor desc(option, persistent, true, "", user_context);
+ return (make_pair(desc, space_param));
+ }
+
+ // Transform string of hexadecimal digits into binary format.
+ std::vector<uint8_t> binary;
+ std::vector<std::string> data_tokens;
+
+ // If the definition is available and csv-format hasn't been explicitly
+ // disabled, we will parse the data as comma separated values.
+ if (def && (csv_format_param.unspecified() || csv_format_param)) {
+ // If the option data is specified as a string of comma
+ // separated values then we need to split this string into
+ // individual values - each value will be used to initialize
+ // one data field of an option.
+ // It is the only usage of the escape option: this allows
+ // to embed commas in individual values and to return
+ // for instance a string value with embedded commas.
+ data_tokens = isc::util::str::tokens(data_param, ",", true);
+
+ } else {
+ // Try to convert the values in quotes into a vector of ASCII codes.
+ // If the identifier lacks opening and closing quote, this will return
+ // an empty value, in which case we'll try to decode it as a string of
+ // hexadecimal digits.
+ try {
+ binary = util::str::quotedStringToBinary(data_param);
+ if (binary.empty()) {
+ util::str::decodeFormattedHexString(data_param, binary);
+ }
+ } catch (...) {
+ isc_throw(DhcpConfigError, "option data is not a valid"
+ << " string of hexadecimal digits: " << data_param
+ << " ("
+ << getPosition("data", option_data)
+ << ")");
+ }
+ }
+
+ OptionDescriptor desc(false, false);
+
+ if (!def) {
+ // @todo We have a limited set of option definitions initialized at
+ // the moment. In the future we want to initialize option definitions
+ // for all options. Consequently an error will be issued if an option
+ // definition does not exist for a particular option code. For now it is
+ // ok to create generic option if definition does not exist.
+ OptionPtr option(new Option(universe, static_cast<uint16_t>(code_param),
+ binary));
+
+ desc.option_ = option;
+ desc.persistent_ = !persist_param.unspecified() && persist_param;
+ desc.cancelled_ = !cancel_param.unspecified() && cancel_param;
+ } else {
+ // Option definition has been found so let's use it to create
+ // an instance of our option.
+ try {
+ bool use_csv = csv_format_param.unspecified() || csv_format_param;
+ OptionPtr option = use_csv ?
+ def->optionFactory(universe, def->getCode(), data_tokens) :
+ def->optionFactory(universe, def->getCode(), binary);
+ desc.option_ = option;
+ desc.persistent_ = !persist_param.unspecified() && persist_param;
+ desc.cancelled_ = !cancel_param.unspecified() && cancel_param;
+ if (use_csv) {
+ desc.formatted_value_ = data_param;
+ }
+ } catch (const isc::Exception& ex) {
+ isc_throw(DhcpConfigError, "option data does not match"
+ << " option definition (space: " << space_param
+ << ", code: " << def->getCode() << "): "
+ << ex.what() << " ("
+ << getPosition("data", option_data)
+ << ")");
+ }
+ }
+
+ // Check PAD and END in (and only in) dhcp4 space.
+ if (space_param == DHCP4_OPTION_SPACE) {
+ if (desc.option_->getType() == DHO_PAD) {
+ isc_throw(DhcpConfigError, "invalid option code '0': "
+ << "reserved for PAD ("
+ << option_data->getPosition() << ")");
+ } else if (desc.option_->getType() == DHO_END) {
+ isc_throw(DhcpConfigError, "invalid option code '255': "
+ << "reserved for END ("
+ << option_data->getPosition() << ")");
+ }
+ }
+
+ // For dhcp6 space the value 0 is reserved.
+ if (space_param == DHCP6_OPTION_SPACE) {
+ if (desc.option_->getType() == 0) {
+ isc_throw(DhcpConfigError, "invalid option code '0': "
+ << "reserved value ("
+ << option_data->getPosition() << ")");
+ }
+ }
+
+
+ // Add user context
+ if (user_context) {
+ desc.setContext(user_context);
+ }
+
+ // All went good, so we can set the option space name.
+ return (make_pair(desc, space_param));
+}
+
+// **************************** OptionDataListParser *************************
+OptionDataListParser::OptionDataListParser(//const std::string&,
+ //const CfgOptionPtr& cfg,
+ const uint16_t address_family,
+ CfgOptionDefPtr cfg_option_def)
+ : address_family_(address_family), cfg_option_def_(cfg_option_def) {
+}
+
+
+void OptionDataListParser::parse(const CfgOptionPtr& cfg,
+ isc::data::ConstElementPtr option_data_list,
+ bool encapsulate) {
+ auto option_parser = createOptionDataParser();
+ BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
+ std::pair<OptionDescriptor, std::string> option =
+ option_parser->parse(data);
+ // Use the option description to keep the formatted value
+ cfg->add(option.first, option.second);
+ if (encapsulate) {
+ cfg->encapsulate();
+ }
+ }
+}
+
+boost::shared_ptr<OptionDataParser>
+OptionDataListParser::createOptionDataParser() const {
+ auto parser = boost::make_shared<OptionDataParser>(address_family_, cfg_option_def_);
+ return (parser);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/option_data_parser.h b/src/lib/dhcpsrv/parsers/option_data_parser.h
new file mode 100644
index 0000000..f6b44b3
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/option_data_parser.h
@@ -0,0 +1,240 @@
+// Copyright (C) 2017-2023 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 OPTION_DATA_PARSER_H
+#define OPTION_DATA_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcp/option_definition.h>
+#include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/cfg_option_def.h>
+#include <boost/shared_ptr.hpp>
+#include <util/optional.h>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for option data value.
+///
+/// This parser parses configuration entries that specify value of
+/// a single option. These entries include option name, option code
+/// and data carried by the option. The option data can be specified
+/// in one of the two available formats: binary value represented as
+/// a string of hexadecimal digits or a list of comma separated values.
+/// The format being used is controlled by csv-format configuration
+/// parameter. When setting this value to True, the latter format is
+/// used. The subsequent values in the CSV format apply to relevant
+/// option data fields in the configured option. For example the
+/// configuration: "data" : "192.168.2.0, 56, hello world" can be
+/// used to set values for the option comprising IPv4 address,
+/// integer and string data field. Note that order matters. If the
+/// order of values does not match the order of data fields within
+/// an option the configuration will not be accepted. If parsing
+/// is successful then an instance of an option is created and
+/// added to the storage provided by the calling class.
+class OptionDataParser : public isc::data::SimpleParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param address_family Address family: @c AF_INET or @c AF_INET6.
+ /// @param cfg_option_def Config option definitions (optional)
+ OptionDataParser(const uint16_t address_family,
+ CfgOptionDefPtr cfg_option_def = CfgOptionDefPtr());
+
+ /// @brief Virtual destructor.
+ virtual ~OptionDataParser() {
+ }
+
+ /// @brief Parses ElementPtr containing option definition
+ ///
+ /// This method parses ElementPtr containing the option definition,
+ /// instantiates the option for it and then returns a pair
+ /// of option descriptor (that holds that new option) and
+ /// a string that specifies the option space.
+ ///
+ /// Note: ElementPtr is expected to contain all fields. If your
+ /// ElementPtr does not have them, please use
+ /// @ref isc::data::SimpleParser::setDefaults to fill the missing fields
+ /// with default values.
+ ///
+ /// @param single_option ElementPtr containing option definition
+ /// @return Option object wrapped in option description and an option
+ /// space
+ std::pair<OptionDescriptor, std::string>
+ parse(isc::data::ConstElementPtr single_option);
+
+protected:
+
+ /// @brief Finds an option definition within an option space
+ ///
+ /// Given an option space and an option code, find the corresponding
+ /// option definition within the option definition storage.
+ ///
+ /// @param option_space name of the parameter option space
+ /// @param option_code option code to be used to find the option
+ /// definition, if the option name is unspecified.
+ /// @param option_name option name to be used to lookup the option
+ /// definition.
+ ///
+ /// @return OptionDefinitionPtr of the option definition or an
+ /// empty OptionDefinitionPtr if not found.
+ /// @throw DhcpConfigError if the option space requested is not valid
+ /// for this server.
+ virtual OptionDefinitionPtr
+ findOptionDefinition(const std::string& option_space,
+ const util::Optional<uint32_t>& option_code,
+ const util::Optional<std::string>& option_name) const;
+
+ /// @brief Create option instance.
+ ///
+ /// Creates an instance of an option and adds it to the provided
+ /// options storage. If the option data parsed by createOption function
+ /// is invalid or insufficient this function emits an exception.
+ ///
+ /// If the option data is given as a string containing a hexadecimal
+ /// literal, then it is converted into binary format. These literals
+ /// may contain upper and lower case digits. They may be octets
+ /// delimited by colons or spaces (octets may be 1 or 2 digits)
+ /// If not delimited octets then they must be a continuous string of
+ /// digits with or without a "0x" prefix. Examples:
+ ///
+ /// -# ab:cd:ef - colon delimited
+ /// -# ab cd ef - space delimited
+ /// -# 0xabcdef - 0x prefixed (no delimiters)
+ /// -# abcdef - no prefix or delimiters
+ ///
+ /// A leading zero is assumed for odd number of digits
+ /// in an octet or continuous string.
+ ///
+ /// @param option_data An element holding data for a single option being
+ /// created.
+ ///
+ /// @return created option descriptor
+ ///
+ /// @throw DhcpConfigError if parameters provided in the configuration
+ /// are invalid.
+ std::pair<OptionDescriptor, std::string>
+ createOption(isc::data::ConstElementPtr option_data);
+
+ /// @brief Retrieves parsed option code as an optional value.
+ ///
+ /// @param parent A data element holding full option data configuration.
+ ///
+ /// @return Option code, possibly unspecified.
+ /// @throw DhcpConfigError if option code is invalid.
+ util::Optional<uint32_t>
+ extractCode(data::ConstElementPtr parent) const;
+
+ /// @brief Retrieves parsed option name as an optional value.
+ ///
+ /// @param parent A data element holding full option data configuration.
+ ///
+ /// @return Option name, possibly unspecified.
+ /// @throw DhcpConfigError if option name is invalid.
+ util::Optional<std::string>
+ extractName(data::ConstElementPtr parent) const;
+
+ /// @brief Retrieves csv-format parameter as an optional value.
+ ///
+ /// @return Value of the csv-format parameter, possibly unspecified.
+ util::Optional<bool> extractCSVFormat(data::ConstElementPtr parent) const;
+
+ /// @brief Retrieves option data as a string.
+ ///
+ /// @param parent A data element holding full option data configuration.
+ /// @return Option data as a string. It will return empty string if
+ /// option data is unspecified.
+ util::Optional<std::string>
+ extractData(data::ConstElementPtr parent) const;
+
+ /// @brief Retrieves option space name.
+ ///
+ /// If option space name is not specified in the configuration the
+ /// 'dhcp4' or 'dhcp6' option space name is returned, depending on
+ /// the universe specified in the parser context.
+ ///
+ /// @param parent A data element holding full option data configuration.
+ ///
+ /// @return Option space name.
+ std::string extractSpace(data::ConstElementPtr parent) const;
+
+ /// @brief Retrieves persistent/always-send parameter as an optional value.
+ ///
+ /// @return Value of the persistent parameter, possibly unspecified.
+ util::Optional<bool> extractPersistent(data::ConstElementPtr parent) const;
+
+ /// @brief Retrieves cancelled/never-send parameter as an optional value.
+ ///
+ /// @return Value of the cancelled parameter, possibly unspecified.
+ util::Optional<bool> extractCancelled(data::ConstElementPtr parent) const;
+
+ /// @brief Address family: @c AF_INET or @c AF_INET6.
+ uint16_t address_family_;
+
+ /// @brief Config option definitions
+ CfgOptionDefPtr cfg_option_def_;
+};
+
+/// @brief Parser for option data values within a subnet.
+///
+/// This parser iterates over all entries that define options
+/// data for a particular subnet and creates a collection of options.
+/// If parsing is successful, all these options are added to the Subnet
+/// object.
+class OptionDataListParser : public isc::data::SimpleParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param address_family Address family: @c AF_INET or AF_INET6
+ /// @param cfg_option_def Config option definitions (optional)
+ OptionDataListParser(const uint16_t address_family,
+ CfgOptionDefPtr cfg_option_def = CfgOptionDefPtr());
+
+ /// @brief Virtual destructor.
+ virtual ~OptionDataListParser() {
+ }
+
+ /// @brief Parses a list of options, instantiates them and stores in cfg
+ ///
+ /// This method expects to get a list of options in option_data_list,
+ /// iterates over them, creates option objects, wraps them with
+ /// option descriptor and stores in specified cfg.
+ ///
+ /// @param cfg created options will be stored here
+ /// @param option_data_list configuration that describes the options
+ /// @param encapsulate a boolean value indicating whether or not the
+ /// parser should encapsulate options with suboptions. The default
+ /// value is true (encapsulate).
+ void parse(const CfgOptionPtr& cfg,
+ isc::data::ConstElementPtr option_data_list,
+ bool encapsulate = true);
+protected:
+
+ /// @brief Returns an instance of the @c OptionDataListParser to
+ /// be used in parsing options.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for option data.
+ ///
+ /// @return an instance of the @c OptionDataListParser.
+ virtual boost::shared_ptr<OptionDataParser> createOptionDataParser() const;
+
+ /// @brief Address family: @c AF_INET or @c AF_INET6
+ uint16_t address_family_;
+
+ /// @brief Config option definitions
+ CfgOptionDefPtr cfg_option_def_;
+};
+
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // OPTION_DATA_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/sanity_checks_parser.cc b/src/lib/dhcpsrv/parsers/sanity_checks_parser.cc
new file mode 100644
index 0000000..e7c0b20
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/sanity_checks_parser.cc
@@ -0,0 +1,80 @@
+// Copyright (C) 2018-2022 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 <dhcpsrv/parsers/sanity_checks_parser.h>
+#include <dhcpsrv/cfg_consistency.h>
+#include <cc/data.h>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+void
+SanityChecksParser::parse(SrvConfig& cfg, const ConstElementPtr& sanity_checks) {
+
+ if (!sanity_checks) {
+ return;
+ }
+ if (sanity_checks->getType() != Element::map) {
+ isc_throw(DhcpConfigError, "sanity-checks is supposed to be a map");
+ }
+
+ // Subnet-id lease checker.
+ ConstElementPtr checks = sanity_checks->get("lease-checks");
+ if (checks) {
+ if (checks->getType() != Element::string) {
+ isc_throw(DhcpConfigError, "lease-checks must be a string");
+ }
+ std::string lc = checks->stringValue();
+ CfgConsistency::LeaseSanity check;
+ if (lc == "none") {
+ check = CfgConsistency::LEASE_CHECK_NONE;
+ } else if (lc == "warn") {
+ check = CfgConsistency::LEASE_CHECK_WARN;
+ } else if (lc == "fix") {
+ check = CfgConsistency::LEASE_CHECK_FIX;
+ } else if (lc == "fix-del") {
+ check = CfgConsistency::LEASE_CHECK_FIX_DEL;
+ } else if (lc == "del") {
+ check = CfgConsistency::LEASE_CHECK_DEL;
+ } else {
+ isc_throw(DhcpConfigError, "Unsupported lease-checks value: " << lc
+ << ", supported values are: none, warn, fix, fix-del, del");
+ }
+ cfg.getConsistency()->setLeaseSanityCheck(check);
+ }
+
+ // Extended info lease checker.
+ checks = sanity_checks->get("extended-info-checks");
+ if (checks) {
+ if (checks->getType() != Element::string) {
+ isc_throw(DhcpConfigError, "extended-info-checks must be a string");
+ }
+ std::string exc = checks->stringValue();
+ CfgConsistency::ExtendedInfoSanity check;
+ if (exc == "none") {
+ check = CfgConsistency::EXTENDED_INFO_CHECK_NONE;
+ } else if (exc == "fix") {
+ check = CfgConsistency::EXTENDED_INFO_CHECK_FIX;
+ } else if (exc == "strict") {
+ check = CfgConsistency::EXTENDED_INFO_CHECK_STRICT;
+ } else if (exc == "pedantic") {
+ check = CfgConsistency::EXTENDED_INFO_CHECK_PEDANTIC;
+ } else {
+ isc_throw(DhcpConfigError,
+ "Unsupported extended-info-checks value: " << exc
+ << ", supported values are: none, fix, strict, pedantic");
+ }
+ cfg.getConsistency()->setExtendedInfoSanityCheck(check);
+ }
+
+ // Additional sanity check fields will come in later here.
+}
+
+}
+}
diff --git a/src/lib/dhcpsrv/parsers/sanity_checks_parser.h b/src/lib/dhcpsrv/parsers/sanity_checks_parser.h
new file mode 100644
index 0000000..660d612
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/sanity_checks_parser.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2018-2022 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 SANITY_CHECKS_PARSER_H
+#define SANITY_CHECKS_PARSER_H
+
+#include <cc/simple_parser.h>
+#include <dhcpsrv/srv_config.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Simple parser for sanity-checks structure
+///
+/// Currently parses only one parameter:
+/// - lease-checks. Allowed values: none, warn, fix, fix-del, del
+class SanityChecksParser : public isc::data::SimpleParser {
+ public:
+ /// @brief parses JSON structure
+ ///
+ /// @param srv_cfg parsed value will be stored here
+ /// @param value a JSON map that contains lease-checks parameter.
+ void parse(SrvConfig& srv_cfg, const isc::data::ConstElementPtr& value);
+};
+
+}
+}
+
+#endif /* SANITY_CHECKS_PARSER_H */
diff --git a/src/lib/dhcpsrv/parsers/shared_network_parser.cc b/src/lib/dhcpsrv/parsers/shared_network_parser.cc
new file mode 100644
index 0000000..ef9cc1d
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/shared_network_parser.cc
@@ -0,0 +1,429 @@
+// Copyright (C) 2017-2023 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 <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <dhcpsrv/parsers/shared_network_parser.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
+#include <dhcpsrv/shared_network.h>
+#include <boost/make_shared.hpp>
+#include <boost/pointer_cast.hpp>
+#include <string>
+
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+SharedNetwork4Parser::SharedNetwork4Parser(bool check_iface)
+ : check_iface_(check_iface) {
+}
+
+SharedNetwork4Ptr
+SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data,
+ bool encapsulate_options) {
+ SharedNetwork4Ptr shared_network;
+ try {
+
+ // Check parameters.
+ checkKeywords(SimpleParser4::SHARED_NETWORK4_PARAMETERS,
+ shared_network_data);
+
+ // Make sure that the network name has been specified. The name is required
+ // to create a SharedNetwork4 object.
+ std::string name = getString(shared_network_data, "name");
+ shared_network.reset(new SharedNetwork4(name));
+
+ // Move from reservation mode to new reservations flags.
+ ElementPtr mutable_params;
+ mutable_params = boost::const_pointer_cast<Element>(shared_network_data);
+ BaseNetworkParser::moveReservationMode(mutable_params);
+
+ // Parse parameters common to all Network derivations.
+ NetworkPtr network = boost::dynamic_pointer_cast<Network>(shared_network);
+ parseCommon(mutable_params, network);
+
+ // interface is an optional parameter
+ if (shared_network_data->contains("interface")) {
+ std::string iface = getString(shared_network_data, "interface");
+ if (!iface.empty()) {
+ if (check_iface_ && !IfaceMgr::instance().getIface(iface)) {
+ ConstElementPtr error =
+ shared_network_data->get("interface");
+ isc_throw(DhcpConfigError,
+ "Specified network interface name " << iface
+ << " for shared network " << name
+ << " is not present in the system ("
+ << error->getPosition() << ")");
+ }
+ shared_network->setIface(iface);
+ }
+ }
+
+ if (shared_network_data->contains("option-data")) {
+ auto json = shared_network_data->get("option-data");
+ // Create parser instance for option-data.
+ CfgOptionPtr cfg_option = shared_network->getCfgOption();
+ auto parser = createOptionDataListParser();
+ parser->parse(cfg_option, json, encapsulate_options);
+ }
+
+ if (shared_network_data->contains("subnet4")) {
+ auto json = shared_network_data->get("subnet4");
+
+ // Create parser instance of subnet4.
+ auto parser = createSubnetsListParser();
+ Subnet4Collection subnets;
+ parser->parse(subnets, json);
+
+ // Add all returned subnets into shared network.
+ for (auto subnet = subnets.cbegin(); subnet != subnets.cend();
+ ++subnet) {
+ shared_network->add(*subnet);
+ }
+ }
+
+ if (shared_network_data->contains("match-client-id")) {
+ shared_network->setMatchClientId(getBoolean(shared_network_data,
+ "match-client-id"));
+ }
+
+ if (shared_network_data->contains("authoritative")) {
+ shared_network->setAuthoritative(getBoolean(shared_network_data,
+ "authoritative"));
+ }
+
+ // Set next-server
+ if (shared_network_data->contains("next-server")) {
+ std::string next_server;
+ try {
+ next_server = getString(shared_network_data, "next-server");
+ if (!next_server.empty()) {
+ shared_network->setSiaddr(IOAddress(next_server));
+ }
+ } catch (...) {
+ ConstElementPtr next = shared_network_data->get("next-server");
+ std::string pos;
+ if (next) {
+ pos = next->getPosition().str();
+ } else {
+ pos = shared_network_data->getPosition().str();
+ }
+ isc_throw(DhcpConfigError, "invalid parameter next-server : "
+ << next_server << "(" << pos << ")");
+ }
+ }
+
+ // Set server-hostname.
+ if (shared_network_data->contains("server-hostname")) {
+ std::string sname = getString(shared_network_data, "server-hostname");
+ if (!sname.empty()) {
+ if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
+ ConstElementPtr error = shared_network_data->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() << ")");
+ }
+ shared_network->setSname(sname);
+ }
+ }
+
+ // Set boot-file-name.
+ if (shared_network_data->contains("boot-file-name")) {
+ std::string filename = getString(shared_network_data, "boot-file-name");
+ if (!filename.empty()) {
+ if (filename.length() > Pkt4::MAX_FILE_LEN) {
+ ConstElementPtr error = shared_network_data->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() << ")");
+ }
+ shared_network->setFilename(filename);
+ }
+ }
+
+ if (shared_network_data->contains("client-class")) {
+ std::string client_class = getString(shared_network_data, "client-class");
+ if (!client_class.empty()) {
+ shared_network->allowClientClass(client_class);
+ }
+ }
+
+ ConstElementPtr user_context = shared_network_data->get("user-context");
+ if (user_context) {
+ shared_network->setContext(user_context);
+ }
+
+ if (shared_network_data->contains("require-client-classes")) {
+ const std::vector<data::ElementPtr>& class_list =
+ shared_network_data->get("require-client-classes")->listValue();
+ for (auto cclass = class_list.cbegin();
+ cclass != class_list.cend(); ++cclass) {
+ if (((*cclass)->getType() != Element::string) ||
+ (*cclass)->stringValue().empty()) {
+ isc_throw(DhcpConfigError, "invalid class name ("
+ << (*cclass)->getPosition() << ")");
+ }
+ shared_network->requireClientClass((*cclass)->stringValue());
+ }
+ }
+
+ if (shared_network_data->contains("relay")) {
+ auto relay_parms = shared_network_data->get("relay");
+ if (relay_parms) {
+ RelayInfoParser parser(Option::V4);
+ Network::RelayInfoPtr relay_info(new Network::RelayInfo());
+ parser.parse(relay_info, relay_parms);
+ shared_network->setRelayInfo(*relay_info);
+ }
+ }
+
+ parseTeePercents(shared_network_data, network);
+
+ // Parse DDNS parameters
+ parseDdnsParams(shared_network_data, network);
+
+ // Parse lease cache parameters
+ parseCacheParams(shared_network_data, network);
+
+ // Parse allocator params.
+ parseAllocatorParams(shared_network_data, network);
+
+ // Parse offer-lifetime parameter.
+ Network4Ptr network4 = boost::dynamic_pointer_cast<Network4>(shared_network);
+ parseOfferLft(shared_network_data, network4);
+
+ } catch (const DhcpConfigError&) {
+ // Position was already added
+ throw;
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << shared_network_data->getPosition() << ")");
+ }
+
+ // In order to take advantage of the dynamic inheritance of global
+ // parameters to a shared network we need to set a callback function
+ // for each shared network to allow for fetching global parameters.
+ shared_network->setFetchGlobalsFn([]() -> ConstCfgGlobalsPtr {
+ return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+ });
+
+ return (shared_network);
+}
+
+boost::shared_ptr<OptionDataListParser>
+SharedNetwork4Parser::createOptionDataListParser() const {
+ auto parser = boost::make_shared<OptionDataListParser>(AF_INET);
+ return (parser);
+}
+
+boost::shared_ptr<Subnets4ListConfigParser>
+SharedNetwork4Parser::createSubnetsListParser() const {
+ auto parser = boost::make_shared<Subnets4ListConfigParser>(check_iface_);
+ return (parser);
+}
+
+SharedNetwork6Parser::SharedNetwork6Parser(bool check_iface)
+ : check_iface_(check_iface) {
+}
+
+SharedNetwork6Ptr
+SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data,
+ bool encapsulate_options) {
+ SharedNetwork6Ptr shared_network;
+ std::string name;
+ try {
+ // Check parameters.
+ checkKeywords(SimpleParser6::SHARED_NETWORK6_PARAMETERS,
+ shared_network_data);
+
+ // Make sure that the network name has been specified. The name is required
+ // to create a SharedNetwork6 object.
+ std::string name = getString(shared_network_data, "name");
+ shared_network.reset(new SharedNetwork6(name));
+
+ // Move from reservation mode to new reservations flags.
+ ElementPtr mutable_params;
+ mutable_params = boost::const_pointer_cast<Element>(shared_network_data);
+ BaseNetworkParser::moveReservationMode(mutable_params);
+
+ // Parse parameters common to all Network derivations.
+ NetworkPtr network = boost::dynamic_pointer_cast<Network>(shared_network);
+ parseCommon(mutable_params, network);
+
+ // preferred-lifetime
+ shared_network->setPreferred(parseIntTriplet(shared_network_data,
+ "preferred-lifetime"));
+
+ // Get interface-id option content. For now we support string
+ // representation only
+ Optional<std::string> ifaceid;
+ if (shared_network_data->contains("interface-id")) {
+ ifaceid = getString(shared_network_data, "interface-id");
+ }
+
+ // Interface is an optional parameter
+ Optional<std::string> iface;
+ if (shared_network_data->contains("interface")) {
+ iface = getString(shared_network_data, "interface");
+ }
+
+ // Specifying both interface for locally reachable subnets and
+ // interface id for relays is mutually exclusive. Need to test for
+ // this condition.
+ 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"
+ " via relays) cannot be defined at the same time for "
+ "shared network " << name << "("
+ << shared_network_data->getPosition() << ")");
+ }
+
+ // Configure interface-id for remote interfaces, if defined
+ 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));
+ shared_network->setInterfaceId(opt);
+ }
+
+ // Set interface name. If it is defined, then subnets are available
+ // directly over specified network interface.
+ if (!iface.unspecified() && !iface.empty()) {
+ if (check_iface_ && !IfaceMgr::instance().getIface(iface)) {
+ ConstElementPtr error = shared_network_data->get("interface");
+ isc_throw(DhcpConfigError,
+ "Specified network interface name " << iface
+ << " for shared network " << name
+ << " is not present in the system ("
+ << error->getPosition() << ")");
+ }
+ shared_network->setIface(iface);
+ }
+
+ if (shared_network_data->contains("rapid-commit")) {
+ shared_network->setRapidCommit(getBoolean(shared_network_data,
+ "rapid-commit"));
+ }
+
+ if (shared_network_data->contains("option-data")) {
+ auto json = shared_network_data->get("option-data");
+ // Create parser instance for option-data.
+ CfgOptionPtr cfg_option = shared_network->getCfgOption();
+ auto parser = createOptionDataListParser();
+ parser->parse(cfg_option, json, encapsulate_options);
+ }
+
+ if (shared_network_data->contains("client-class")) {
+ std::string client_class = getString(shared_network_data, "client-class");
+ if (!client_class.empty()) {
+ shared_network->allowClientClass(client_class);
+ }
+ }
+
+ ConstElementPtr user_context = shared_network_data->get("user-context");
+ if (user_context) {
+ shared_network->setContext(user_context);
+ }
+
+ if (shared_network_data->contains("require-client-classes")) {
+ const std::vector<data::ElementPtr>& class_list =
+ shared_network_data->get("require-client-classes")->listValue();
+ for (auto cclass = class_list.cbegin();
+ cclass != class_list.cend(); ++cclass) {
+ if (((*cclass)->getType() != Element::string) ||
+ (*cclass)->stringValue().empty()) {
+ isc_throw(DhcpConfigError, "invalid class name ("
+ << (*cclass)->getPosition() << ")");
+ }
+ shared_network->requireClientClass((*cclass)->stringValue());
+ }
+ }
+
+ if (shared_network_data->contains("subnet6")) {
+ auto json = shared_network_data->get("subnet6");
+
+ // Create parser instance of subnet6.
+ auto parser = createSubnetsListParser();
+ Subnet6Collection subnets;
+ parser->parse(subnets, json);
+
+ // Add all returned subnets into shared network.
+ for (auto subnet = subnets.cbegin(); subnet != subnets.cend();
+ ++subnet) {
+ shared_network->add(*subnet);
+ }
+ }
+
+ if (shared_network_data->contains("relay")) {
+ auto relay_parms = shared_network_data->get("relay");
+ if (relay_parms) {
+ RelayInfoParser parser(Option::V6);
+ Network::RelayInfoPtr relay_info(new Network::RelayInfo());
+ parser.parse(relay_info, relay_parms);
+ shared_network->setRelayInfo(*relay_info);
+ }
+ }
+
+ parseTeePercents(shared_network_data, network);
+
+ // Parse DDNS parameters
+ parseDdnsParams(shared_network_data, network);
+
+ // Parse lease cache parameters
+ parseCacheParams(shared_network_data, network);
+
+ // Parse allocator params.
+ parseAllocatorParams(shared_network_data, network);
+ if (network->getAllocatorType() == "flq") {
+ isc_throw(BadValue, "Free Lease Queue allocator is not supported for IPv6 address pools");
+ }
+
+ // Parse prefix delegation allocator params.
+ auto network6 = boost::dynamic_pointer_cast<Network6>(shared_network);
+ parsePdAllocatorParams(shared_network_data, network6);
+
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << shared_network_data->getPosition() << ")");
+ }
+
+ // In order to take advantage of the dynamic inheritance of global
+ // parameters to a shared network we need to set a callback function
+ // for each shared network which can be used to fetch global parameters.
+ shared_network->setFetchGlobalsFn([]() -> ConstCfgGlobalsPtr {
+ return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+ });
+
+ return (shared_network);
+}
+
+boost::shared_ptr<OptionDataListParser>
+SharedNetwork6Parser::createOptionDataListParser() const {
+ auto parser = boost::make_shared<OptionDataListParser>(AF_INET6);
+ return (parser);
+}
+
+boost::shared_ptr<Subnets6ListConfigParser>
+SharedNetwork6Parser::createSubnetsListParser() const {
+ auto parser = boost::make_shared<Subnets6ListConfigParser>(check_iface_);
+ return (parser);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/shared_network_parser.h b/src/lib/dhcpsrv/parsers/shared_network_parser.h
new file mode 100644
index 0000000..b7c78fa
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/shared_network_parser.h
@@ -0,0 +1,126 @@
+// Copyright (C) 2017-2023 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 SHARED_SUBNET_PARSER_H
+#define SHARED_SUBNET_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/cfg_subnets4.h>
+#include <dhcpsrv/cfg_subnets6.h>
+#include <dhcpsrv/shared_network.h>
+#include <dhcpsrv/parsers/base_network_parser.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Implements parser for IPv4 shared networks.
+class SharedNetwork4Parser : public BaseNetworkParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param check_iface Check if the specified interface exists in
+ /// the system.
+ SharedNetwork4Parser(bool check_iface = true);
+
+ /// @brief Virtual destructor.
+ virtual ~SharedNetwork4Parser() {
+ }
+
+ /// @brief Parses shared configuration information for IPv4 shared network.
+ ///
+ /// @param shared_network_data Data element holding shared network
+ /// configuration to be parsed.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ ///
+ /// @return Pointer to an object representing shared network.
+ /// @throw DhcpConfigError when shared network configuration is invalid.
+ SharedNetwork4Ptr
+ parse(const data::ConstElementPtr& shared_network_data,
+ bool encapsulate_options = true);
+
+protected:
+
+ /// @brief Returns an instance of the @c OptionDataListParser to
+ /// be used in parsing the option-data structure.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for option data.
+ ///
+ /// @return an instance of the @c OptionDataListParser(AF_INET).
+ virtual boost::shared_ptr<OptionDataListParser> createOptionDataListParser() const;
+
+ /// @brief Returns an instance of the @c Subnets4ListConfigParser
+ /// to be used for parsing the subnets within the shared network.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the subnets.
+ ///
+ /// @return an instance of the @c Subnets4ListConfigParser.
+ virtual boost::shared_ptr<Subnets4ListConfigParser> createSubnetsListParser() const;
+
+ /// Check if the specified interface exists in the system.
+ bool check_iface_;
+};
+
+/// @brief Implements parser for IPv6 shared networks.
+class SharedNetwork6Parser : public BaseNetworkParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param check_iface Check if the specified interface exists in
+ /// the system.
+ SharedNetwork6Parser(bool check_iface = true);
+
+ /// @brief Virtual destructor.
+ virtual ~SharedNetwork6Parser() {
+ }
+
+ /// @brief Parses shared configuration information for IPv6 shared network.
+ ///
+ /// @param shared_network_data Data element holding shared network
+ /// configuration to be parsed.
+ /// @param encapsulate_options a boolean parameter indicating if the
+ /// parsed options should be encapsulated with suboptions.
+ ///
+ /// @return Pointer to an object representing shared network.
+ /// @throw DhcpConfigError when shared network configuration is invalid.
+ SharedNetwork6Ptr
+ parse(const data::ConstElementPtr& shared_network_data,
+ bool encapsulate_options = true);
+
+protected:
+
+ /// @brief Returns an instance of the @c OptionDataListParser to
+ /// be used in parsing the option-data structure.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for option data.
+ ///
+ /// @return an instance of the @c OptionDataListParser(AF_INET6).
+ virtual boost::shared_ptr<OptionDataListParser> createOptionDataListParser() const;
+
+ /// @brief Returns an instance of the @c Subnets6ListConfigParser
+ /// to be used for parsing the subnets within the shared network.
+ ///
+ /// This function can be overridden in the child classes to supply
+ /// a custom parser for the subnets.
+ ///
+ /// @return an instance of the @c Subnets6ListConfigParser.
+ virtual boost::shared_ptr<Subnets6ListConfigParser> createSubnetsListParser() const;
+
+ /// Check if the specified interface exists in the system.
+ bool check_iface_;
+};
+
+} // enf of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // SHARED_SUBNET_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/shared_networks_list_parser.h b/src/lib/dhcpsrv/parsers/shared_networks_list_parser.h
new file mode 100644
index 0000000..5f49c8b
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/shared_networks_list_parser.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2017-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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef SHARED_NETWORKS_LIST_PARSER_H
+#define SHARED_NETWORKS_LIST_PARSER_H
+
+#include <cc/data.h>
+#include <cc/dhcp_config_error.h>
+#include <cc/simple_parser.h>
+#include <exceptions/exceptions.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/parsers/shared_network_parser.h>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for a list of shared networks.
+///
+/// This is a generic parser for a list of IPv4 or IPv6 shared networks.
+///
+/// @tparam SharedNetworkParserType Type of the parser to be used for
+/// parsing shared network, i.e. @ref SharedNetwork4Parser or
+/// @ref SharedNetwork6Parser.
+template<typename SharedNetworkParserType>
+class SharedNetworksListParser : public data::SimpleParser {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param check_iface Check if the specified interface exists in
+ /// the system.
+ SharedNetworksListParser(bool check_iface = true)
+ : check_iface_(check_iface) {
+ }
+
+ /// @brief Parses a list of shared networks.
+ ///
+ /// @tparam CfgSharedNetworksTypePtr Type of the configuration structure
+ /// into which the result will be stored, i.e. @ref CfgSharedNetworks4
+ /// or @ref CfgSharedNetworks6.
+ /// @param [out] cfg Shared networks configuration structure into which
+ /// the data should be parsed.
+ /// @param shared_networks_list_data List element holding a list of
+ /// shared networks.
+ ///
+ /// @throw DhcpConfigError when error has occurred, e.g. when networks
+ /// with duplicated names have been specified.
+ template<typename CfgSharedNetworksTypePtr>
+ void parse(CfgSharedNetworksTypePtr& cfg,
+ const data::ConstElementPtr& shared_networks_list_data) {
+ try {
+ // Get the C++ vector holding networks.
+ const std::vector<data::ElementPtr>& networks_list =
+ shared_networks_list_data->listValue();
+ // Iterate over all networks and do the parsing.
+ for (auto network_element = networks_list.cbegin();
+ network_element != networks_list.cend(); ++network_element) {
+ SharedNetworkParserType parser(check_iface_);
+ auto network = parser.parse(*network_element);
+ cfg->add(network);
+ }
+ } catch (const DhcpConfigError&) {
+ // Such exceptions are emitted by the lower level parsers and
+ // errors should already include element's positions. So, we
+ // simply rethrow.
+ throw;
+
+ } catch (const std::exception& ex) {
+ // Other exceptions don't include positions of the elements, so
+ // we should append one.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << shared_networks_list_data->getPosition() << ")");
+ }
+ }
+
+protected:
+ /// Check if the specified interface exists in the system.
+ bool check_iface_;
+};
+
+/// @brief Type of the shared networks list parser for IPv4.
+typedef SharedNetworksListParser<SharedNetwork4Parser> SharedNetworks4ListParser;
+
+/// @brief Type of the shared networks list parser for IPv6.
+typedef SharedNetworksListParser<SharedNetwork6Parser> SharedNetworks6ListParser;
+
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // SHARED_NETWORKS_LIST_PARSER_H
diff --git a/src/lib/dhcpsrv/parsers/simple_parser4.cc b/src/lib/dhcpsrv/parsers/simple_parser4.cc
new file mode 100644
index 0000000..f9349ef
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/simple_parser4.cc
@@ -0,0 +1,548 @@
+// Copyright (C) 2016-2023 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 <dhcpsrv/parsers/simple_parser4.h>
+#include <cc/data.h>
+#include <boost/foreach.hpp>
+#include <iostream>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+/// @brief This sets of arrays define the default values and
+/// values inherited (derived) between various scopes.
+///
+/// Each of those is documented in @file simple_parser4.cc. This
+/// is different than most other comments in Kea code. The reason
+/// for placing those in .cc rather than .h file is that it
+/// is expected to be one centralized place to look at for
+/// the default values. This is expected to be looked at also by
+/// people who are not skilled in C or C++, so they may be
+/// confused with the differences between declaration and definition.
+/// As such, there's one file to look at that hopefully is readable
+/// without any C or C++ skills.
+///
+/// @{
+
+/// @brief This table defines all global parameters in DHCPv4.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows global_param rule in bison grammar.
+const SimpleKeywords SimpleParser4::GLOBAL4_PARAMETERS = {
+ { "valid-lifetime", Element::integer },
+ { "min-valid-lifetime", Element::integer },
+ { "max-valid-lifetime", Element::integer },
+ { "renew-timer", Element::integer },
+ { "rebind-timer", Element::integer },
+ { "decline-probation-period", Element::integer },
+ { "subnet4", Element::list },
+ { "shared-networks", Element::list },
+ { "interfaces-config", Element::map },
+ { "lease-database", Element::map },
+ { "hosts-database", Element::map },
+ { "hosts-databases", Element::list },
+ { "host-reservation-identifiers", Element::list },
+ { "client-classes", Element::list },
+ { "option-def", Element::list },
+ { "option-data", Element::list },
+ { "hooks-libraries", Element::list },
+ { "expired-leases-processing", Element::map },
+ { "dhcp4o6-port", Element::integer },
+ { "control-socket", Element::map },
+ { "dhcp-queue-control", Element::map },
+ { "dhcp-ddns", Element::map },
+ { "echo-client-id", Element::boolean },
+ { "match-client-id", Element::boolean },
+ { "authoritative", Element::boolean },
+ { "next-server", Element::string },
+ { "server-hostname", Element::string },
+ { "boot-file-name", Element::string },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "sanity-checks", Element::map },
+ { "reservations", Element::list },
+ { "config-control", Element::map },
+ { "server-tag", Element::string },
+ { "reservation-mode", Element::string },
+ { "reservations-global", Element::boolean },
+ { "reservations-in-subnet", Element::boolean },
+ { "reservations-out-of-pool", Element::boolean },
+ { "calculate-tee-times", Element::boolean },
+ { "t1-percent", Element::real },
+ { "t2-percent", Element::real },
+ { "loggers", Element::list },
+ { "hostname-char-set", Element::string },
+ { "hostname-char-replacement", Element::string },
+ { "ddns-send-updates", Element::boolean },
+ { "ddns-override-no-update", Element::boolean },
+ { "ddns-override-client-update", Element::boolean },
+ { "ddns-replace-client-name", Element::string },
+ { "ddns-generated-prefix", Element::string },
+ { "ddns-qualifying-suffix", Element::string },
+ { "store-extended-info", Element::boolean },
+ { "statistic-default-sample-count", Element::integer },
+ { "statistic-default-sample-age", Element::integer },
+ { "multi-threading", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "early-global-reservations-lookup", Element::boolean },
+ { "ip-reservations-unique", Element::boolean },
+ { "reservations-lookup-first", Element::boolean },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean },
+ { "compatibility", Element::map },
+ { "parked-packet-limit", Element::integer },
+ { "allocator", Element::string },
+ { "offer-lifetime", Element::integer },
+ { "ddns-ttl-percent", Element::real },
+};
+
+/// @brief This table defines default global values for DHCPv4
+///
+/// Some of the global parameters defined in the global scope (i.e. directly
+/// in Dhcp4) are optional. If not defined, the following values will be
+/// used.
+const SimpleDefaults SimpleParser4::GLOBAL4_DEFAULTS = {
+ { "valid-lifetime", Element::integer, "7200" },
+ { "decline-probation-period", Element::integer, "86400" }, // 24h
+ { "dhcp4o6-port", Element::integer, "0" },
+ { "echo-client-id", Element::boolean, "true" },
+ { "match-client-id", Element::boolean, "true" },
+ { "authoritative", Element::boolean, "false" },
+ { "next-server", Element::string, "0.0.0.0" },
+ { "server-hostname", Element::string, "" },
+ { "boot-file-name", Element::string, "" },
+ { "server-tag", Element::string, "" },
+ { "reservations-global", Element::boolean, "false" },
+ { "reservations-in-subnet", Element::boolean, "true" },
+ { "reservations-out-of-pool", Element::boolean, "false" },
+ { "calculate-tee-times", Element::boolean, "false" },
+ { "t1-percent", Element::real, ".50" },
+ { "t2-percent", Element::real, ".875" },
+ { "ddns-send-updates", Element::boolean, "true" },
+ { "ddns-override-no-update", Element::boolean, "false" },
+ { "ddns-override-client-update", Element::boolean, "false" },
+ { "ddns-replace-client-name", Element::string, "never" },
+ { "ddns-generated-prefix", Element::string, "myhost" },
+ { "ddns-qualifying-suffix", Element::string, "" },
+ { "hostname-char-set", Element::string, "[^A-Za-z0-9.-]" },
+ { "hostname-char-replacement", Element::string, "" },
+ { "store-extended-info", Element::boolean, "false" },
+ { "statistic-default-sample-count", Element::integer, "20" },
+ { "statistic-default-sample-age", Element::integer, "0" },
+ { "early-global-reservations-lookup", Element::boolean, "false" },
+ { "ip-reservations-unique", Element::boolean, "true" },
+ { "reservations-lookup-first", Element::boolean, "false" },
+ { "ddns-update-on-renew", Element::boolean, "false" },
+ { "ddns-use-conflict-resolution", Element::boolean, "true" },
+ { "parked-packet-limit", Element::integer, "256" },
+ { "allocator", Element::string, "iterative" },
+};
+
+/// @brief This table defines all option definition parameters.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows option_def_param rules in bison grammar.
+const SimpleKeywords SimpleParser4::OPTION4_DEF_PARAMETERS = {
+ { "name", Element::string },
+ { "code", Element::integer },
+ { "type", Element::string },
+ { "record-types", Element::string },
+ { "space", Element::string },
+ { "encapsulate", Element::string },
+ { "array", Element::boolean, },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "metadata", Element::map }
+};
+
+/// @brief This table defines default values for option definitions in DHCPv4.
+///
+/// Dhcp4 may contain an array called option-def that enumerates new option
+/// definitions. This array lists default values for those option definitions.
+const SimpleDefaults SimpleParser4::OPTION4_DEF_DEFAULTS = {
+ { "record-types", Element::string, ""},
+ { "space", Element::string, "dhcp4"}, // DHCP4_OPTION_SPACE
+ { "array", Element::boolean, "false"},
+ { "encapsulate", Element::string, "" }
+};
+
+/// @brief This table defines all option parameters.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows option_param rules in bison grammar.
+const SimpleKeywords SimpleParser4::OPTION4_PARAMETERS = {
+ { "name", Element::string },
+ { "data", Element::string },
+ { "code", Element::integer },
+ { "space", Element::string },
+ { "csv-format", Element::boolean },
+ { "always-send", Element::boolean },
+ { "never-send", Element::boolean },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "metadata", Element::map }
+};
+
+/// @brief This table defines default values for options in DHCPv4.
+///
+/// Dhcp4 usually contains option values (option-data) defined in global,
+/// subnet, class or host reservations scopes. This array lists default values
+/// for those option-data declarations.
+const SimpleDefaults SimpleParser4::OPTION4_DEFAULTS = {
+ { "space", Element::string, "dhcp4"}, // DHCP4_OPTION_SPACE
+ { "csv-format", Element::boolean, "true"},
+ { "always-send", Element::boolean, "false"},
+ { "never-send", Element::boolean, "false"}
+};
+
+/// @brief This table defines all subnet parameters for DHCPv4.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows subnet4_param rule in bison grammar.
+const SimpleKeywords SimpleParser4::SUBNET4_PARAMETERS = {
+ { "valid-lifetime", Element::integer },
+ { "min-valid-lifetime", Element::integer },
+ { "max-valid-lifetime", Element::integer },
+ { "renew-timer", Element::integer },
+ { "rebind-timer", Element::integer },
+ { "option-data", Element::list },
+ { "pools", Element::list },
+ { "subnet", Element::string },
+ { "interface", Element::string },
+ { "id", Element::integer },
+ { "client-class", Element::string },
+ { "require-client-classes", Element::list },
+ { "reservations", Element::list },
+ { "reservation-mode", Element::string },
+ { "reservations-global", Element::boolean },
+ { "reservations-in-subnet", Element::boolean },
+ { "reservations-out-of-pool", Element::boolean },
+ { "relay", Element::map },
+ { "match-client-id", Element::boolean },
+ { "authoritative", Element::boolean },
+ { "next-server", Element::string },
+ { "server-hostname", Element::string },
+ { "boot-file-name", Element::string },
+ { "4o6-interface", Element::string },
+ { "4o6-interface-id", Element::string },
+ { "4o6-subnet", Element::string },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "calculate-tee-times", Element::boolean },
+ { "t1-percent", Element::real },
+ { "t2-percent", Element::real },
+ { "ddns-send-updates", Element::boolean },
+ { "ddns-override-no-update", Element::boolean },
+ { "ddns-override-client-update", Element::boolean },
+ { "ddns-replace-client-name", Element::string },
+ { "ddns-generated-prefix", Element::string },
+ { "ddns-qualifying-suffix", Element::string },
+ { "hostname-char-set", Element::string },
+ { "hostname-char-replacement", Element::string },
+ { "store-extended-info", Element::boolean },
+ { "metadata", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean },
+ { "allocator", Element::string },
+ { "offer-lifetime", Element::integer },
+ { "ddns-ttl-percent", Element::real },
+};
+
+/// @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, "" },
+ { "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 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
+ { "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 = {
+ { "client-class", Element::string, "" },
+ { "interface", Element::string, "" }
+};
+
+/// @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.
+///
+/// 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 = {
+ "rebind-timer",
+ "relay",
+ "renew-timer",
+ "valid-lifetime",
+ "min-valid-lifetime",
+ "max-valid-lifetime",
+ "calculate-tee-times",
+ "t1-percent",
+ "t2-percent",
+ "store-extended-info",
+ "cache-threshold",
+ "cache-max-age",
+ "allocator",
+ "offer-lifetime",
+};
+
+/// @brief This table defines all pool parameters.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows pool_param rules in bison grammar.
+const SimpleKeywords SimpleParser4::POOL4_PARAMETERS = {
+ { "pool", Element::string },
+ { "pool-id", Element::integer },
+ { "option-data", Element::list },
+ { "client-class", Element::string },
+ { "require-client-classes", Element::list },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "metadata", Element::map }
+};
+
+/// @brief This table defines all shared network parameters for DHCPv4.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows shared_network_param rule in bison grammar.
+const SimpleKeywords SimpleParser4::SHARED_NETWORK4_PARAMETERS = {
+ { "name", Element::string },
+ { "subnet4", Element::list },
+ { "interface", Element::string },
+ { "renew-timer", Element::integer },
+ { "rebind-timer", Element::integer },
+ { "option-data", Element::list },
+ { "match-client-id", Element::boolean },
+ { "authoritative", Element::boolean },
+ { "next-server", Element::string },
+ { "server-hostname", Element::string },
+ { "boot-file-name", Element::string },
+ { "relay", Element::map },
+ { "reservation-mode", Element::string },
+ { "reservations-global", Element::boolean },
+ { "reservations-in-subnet", Element::boolean },
+ { "reservations-out-of-pool", Element::boolean },
+ { "client-class", Element::string },
+ { "require-client-classes", Element::list },
+ { "valid-lifetime", Element::integer },
+ { "min-valid-lifetime", Element::integer },
+ { "max-valid-lifetime", Element::integer },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "calculate-tee-times", Element::boolean },
+ { "t1-percent", Element::real },
+ { "t2-percent", Element::real },
+ { "ddns-send-updates", Element::boolean },
+ { "ddns-override-no-update", Element::boolean },
+ { "ddns-override-client-update", Element::boolean },
+ { "ddns-replace-client-name", Element::string },
+ { "ddns-generated-prefix", Element::string },
+ { "ddns-qualifying-suffix", Element::string },
+ { "hostname-char-set", Element::string },
+ { "hostname-char-replacement", Element::string },
+ { "store-extended-info", Element::boolean },
+ { "metadata", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean },
+ { "allocator", Element::string },
+ { "offer-lifetime", Element::integer },
+ { "ddns-ttl-percent", Element::real },
+};
+
+/// @brief This table defines default values for interfaces for DHCPv4.
+const SimpleDefaults SimpleParser4::IFACE4_DEFAULTS = {
+ { "re-detect", Element::boolean, "true" }
+};
+
+/// @brief This table defines default values for dhcp-queue-control in DHCPv4.
+const SimpleDefaults SimpleParser4::DHCP_QUEUE_CONTROL4_DEFAULTS = {
+ { "enable-queue", Element::boolean, "false"},
+ { "queue-type", Element::string, "kea-ring4"},
+ { "capacity", Element::integer, "64"}
+};
+
+/// @brief This table defines default values for multi-threading in DHCPv4.
+const SimpleDefaults SimpleParser4::DHCP_MULTI_THREADING4_DEFAULTS = {
+ { "enable-multi-threading", Element::boolean, "true" },
+ { "thread-pool-size", Element::integer, "0" },
+ { "packet-queue-size", Element::integer, "64" }
+};
+
+/// @brief This defines default values for sanity checking for DHCPv4.
+const SimpleDefaults SimpleParser4::SANITY_CHECKS4_DEFAULTS = {
+ { "lease-checks", Element::string, "warn" }
+};
+
+/// @}
+
+/// ---------------------------------------------------------------------------
+/// --- end of default values -------------------------------------------------
+/// ---------------------------------------------------------------------------
+
+size_t SimpleParser4::setAllDefaults(ElementPtr global) {
+ size_t cnt = 0;
+
+ // Set global defaults first.
+ cnt = setDefaults(global, GLOBAL4_DEFAULTS);
+
+ // Now set option definition defaults for each specified option definition
+ ConstElementPtr option_defs = global->get("option-def");
+ if (option_defs) {
+ BOOST_FOREACH(ElementPtr option_def, option_defs->listValue()) {
+ cnt += SimpleParser::setDefaults(option_def, OPTION4_DEF_DEFAULTS);
+ }
+ }
+
+ // Set the defaults for option data
+ ConstElementPtr options = global->get("option-data");
+ if (options) {
+ cnt += setListDefaults(options, OPTION4_DEFAULTS);
+ }
+
+ // Now set the defaults for defined subnets
+ ConstElementPtr subnets = global->get("subnet4");
+ if (subnets) {
+ cnt += setListDefaults(subnets, SUBNET4_DEFAULTS);
+ }
+
+ // Set the defaults for interfaces config
+ ConstElementPtr ifaces_cfg = global->get("interfaces-config");
+ if (ifaces_cfg) {
+ ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(ifaces_cfg);
+ cnt += setDefaults(mutable_cfg, IFACE4_DEFAULTS);
+ }
+
+ // Set defaults for shared networks
+ 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, SHARED_SUBNET4_DEFAULTS);
+ }
+ }
+ }
+
+ // Set the defaults for dhcp-queue-control. If the element isn't
+ // there we'll add it.
+ ConstElementPtr queue_control = global->get("dhcp-queue-control");
+ ElementPtr mutable_cfg;
+ if (queue_control) {
+ mutable_cfg = boost::const_pointer_cast<Element>(queue_control);
+ } else {
+ mutable_cfg = Element::createMap();
+ global->set("dhcp-queue-control", mutable_cfg);
+ }
+
+ cnt += setDefaults(mutable_cfg, DHCP_QUEUE_CONTROL4_DEFAULTS);
+
+ // Set the defaults for multi-threading. If the element isn't there
+ // we'll add it.
+ ConstElementPtr multi_threading = global->get("multi-threading");
+ if (multi_threading) {
+ mutable_cfg = boost::const_pointer_cast<Element>(multi_threading);
+ } else {
+ mutable_cfg = Element::createMap();
+ global->set("multi-threading", mutable_cfg);
+ }
+
+ cnt += setDefaults(mutable_cfg, DHCP_MULTI_THREADING4_DEFAULTS);
+
+ // Set the defaults for sanity-checks. If the element isn't
+ // there we'll add it.
+ ConstElementPtr sanity_checks = global->get("sanity-checks");
+ if (sanity_checks) {
+ mutable_cfg = boost::const_pointer_cast<Element>(sanity_checks);
+ } else {
+ mutable_cfg = Element::createMap();
+ global->set("sanity-checks", mutable_cfg);
+ }
+
+ cnt += setDefaults(mutable_cfg, SANITY_CHECKS4_DEFAULTS);
+
+ return (cnt);
+}
+
+size_t SimpleParser4::deriveParameters(ElementPtr global) {
+ size_t cnt = 0;
+
+ // Now derive global parameters into subnets.
+ ConstElementPtr subnets = global->get("subnet4");
+ if (subnets) {
+ BOOST_FOREACH(ElementPtr single_subnet, subnets->listValue()) {
+ cnt += SimpleParser::deriveParams(global, single_subnet,
+ INHERIT_TO_SUBNET4);
+ }
+ }
+
+ // Deriving parameters for shared networks is a bit more involved.
+ // First, the shared-network level derives from global, and then
+ // subnets within derive from it.
+ ConstElementPtr shared = global->get("shared-networks");
+ if (shared) {
+ BOOST_FOREACH(ElementPtr net, shared->listValue()) {
+ // First try to inherit the parameters from shared network,
+ // if defined there.
+ // Then try to inherit them from global.
+ cnt += SimpleParser::deriveParams(global, net,
+ 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_TO_SUBNET4);
+ }
+ }
+ }
+ }
+
+ return (cnt);
+}
+
+} // namespace dhcp
+} // namespace isc
diff --git a/src/lib/dhcpsrv/parsers/simple_parser4.h b/src/lib/dhcpsrv/parsers/simple_parser4.h
new file mode 100644
index 0000000..e209ab4
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/simple_parser4.h
@@ -0,0 +1,69 @@
+// 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef SIMPLE_PARSER4_H
+#define SIMPLE_PARSER4_H
+
+#include <cc/simple_parser.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief SimpleParser specialized for DHCPv4
+///
+/// This class is a @ref isc::data::SimpleParser dedicated to DHCPv4 parser.
+/// In particular, it contains all the default values and names of the
+/// parameters that are to be derived (inherited) between scopes.
+/// For the actual values, see @file simple_parser4.cc
+class SimpleParser4 : public isc::data::SimpleParser {
+public:
+
+ /// @brief Sets all defaults for DHCPv4 configuration
+ ///
+ /// This method sets global, option data and option definitions defaults.
+ ///
+ /// @param global scope to be filled in with defaults.
+ /// @return number of default values added
+ static size_t setAllDefaults(isc::data::ElementPtr global);
+
+ /// @brief Derives (inherits) all parameters from global to more specific scopes.
+ ///
+ /// This method currently does the following:
+ /// - derives global parameters to subnets (lifetimes for now)
+ /// @param global scope to be modified if needed (subnet4 will be extracted)
+ /// @return number of default values derived
+ static size_t deriveParameters(isc::data::ElementPtr global);
+
+ // see simple_parser4.cc for comments for those parameters
+ static const isc::data::SimpleKeywords GLOBAL4_PARAMETERS;
+ static const isc::data::SimpleDefaults GLOBAL4_DEFAULTS;
+
+ static const isc::data::SimpleKeywords OPTION4_DEF_PARAMETERS;
+ static const isc::data::SimpleDefaults OPTION4_DEF_DEFAULTS;
+
+ static const isc::data::SimpleKeywords OPTION4_PARAMETERS;
+ static const isc::data::SimpleDefaults OPTION4_DEFAULTS;
+
+ static const isc::data::SimpleKeywords SUBNET4_PARAMETERS;
+ static const isc::data::SimpleDefaults SUBNET4_DEFAULTS;
+ static const isc::data::SimpleDefaults SHARED_SUBNET4_DEFAULTS;
+ static const isc::data::ParamsList INHERIT_TO_SUBNET4;
+
+ static const isc::data::SimpleKeywords POOL4_PARAMETERS;
+
+ static const isc::data::SimpleKeywords SHARED_NETWORK4_PARAMETERS;
+ static const isc::data::SimpleDefaults SHARED_NETWORK4_DEFAULTS;
+
+ static const isc::data::SimpleDefaults IFACE4_DEFAULTS;
+ static const isc::data::SimpleDefaults DHCP_QUEUE_CONTROL4_DEFAULTS;
+ static const isc::data::SimpleDefaults DHCP_MULTI_THREADING4_DEFAULTS;
+ static const isc::data::SimpleDefaults SANITY_CHECKS4_DEFAULTS;
+};
+
+} // namespace dhcp
+} // namespace isc
+
+#endif
diff --git a/src/lib/dhcpsrv/parsers/simple_parser6.cc b/src/lib/dhcpsrv/parsers/simple_parser6.cc
new file mode 100644
index 0000000..f7484dd
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/simple_parser6.cc
@@ -0,0 +1,564 @@
+// Copyright (C) 2016-2023 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/parsers/simple_parser6.h>
+
+#include <boost/foreach.hpp>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+/// @brief This sets of arrays define the default values and
+/// values inherited (derived) between various scopes.
+///
+/// Each of those is documented in @file simple_parser6.cc. This
+/// is different than most other comments in Kea code. The reason
+/// for placing those in .cc rather than .h file is that it
+/// is expected to be one centralized place to look at for
+/// the default values. This is expected to be looked at also by
+/// people who are not skilled in C or C++, so they may be
+/// confused with the differences between declaration and definition.
+/// As such, there's one file to look at that hopefully is readable
+/// without any C or C++ skills.
+///
+/// @{
+
+/// @brief This table defines all global parameters in DHCPv6.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows global_param rule in bison grammar.
+const SimpleKeywords SimpleParser6::GLOBAL6_PARAMETERS = {
+ { "data-directory", Element::string },
+ { "preferred-lifetime", Element::integer },
+ { "min-preferred-lifetime", Element::integer },
+ { "max-preferred-lifetime", Element::integer },
+ { "valid-lifetime", Element::integer },
+ { "min-valid-lifetime", Element::integer },
+ { "max-valid-lifetime", Element::integer },
+ { "renew-timer", Element::integer },
+ { "rebind-timer", Element::integer },
+ { "decline-probation-period", Element::integer },
+ { "subnet6", Element::list },
+ { "shared-networks", Element::list },
+ { "interfaces-config", Element::map },
+ { "lease-database", Element::map },
+ { "hosts-database", Element::map },
+ { "hosts-databases", Element::list },
+ { "mac-sources", Element::list },
+ { "relay-supplied-options", Element::list },
+ { "host-reservation-identifiers", Element::list },
+ { "client-classes", Element::list },
+ { "option-def", Element::list },
+ { "option-data", Element::list },
+ { "hooks-libraries", Element::list },
+ { "expired-leases-processing", Element::map },
+ { "server-id", Element::map },
+ { "dhcp4o6-port", Element::integer },
+ { "control-socket", Element::map },
+ { "dhcp-queue-control", Element::map },
+ { "dhcp-ddns", Element::map },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "sanity-checks", Element::map },
+ { "reservations", Element::list },
+ { "config-control", Element::map },
+ { "server-tag", Element::string },
+ { "reservation-mode", Element::string },
+ { "reservations-global", Element::boolean },
+ { "reservations-in-subnet", Element::boolean },
+ { "reservations-out-of-pool", Element::boolean },
+ { "calculate-tee-times", Element::boolean },
+ { "t1-percent", Element::real },
+ { "t2-percent", Element::real },
+ { "loggers", Element::list },
+ { "hostname-char-set", Element::string },
+ { "hostname-char-replacement", Element::string },
+ { "ddns-send-updates", Element::boolean },
+ { "ddns-override-no-update", Element::boolean },
+ { "ddns-override-client-update", Element::boolean },
+ { "ddns-replace-client-name", Element::string },
+ { "ddns-generated-prefix", Element::string },
+ { "ddns-qualifying-suffix", Element::string },
+ { "store-extended-info", Element::boolean },
+ { "statistic-default-sample-count", Element::integer },
+ { "statistic-default-sample-age", Element::integer },
+ { "multi-threading", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "early-global-reservations-lookup", Element::boolean },
+ { "ip-reservations-unique", Element::boolean },
+ { "reservations-lookup-first", Element::boolean },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean },
+ { "compatibility", Element::map },
+ { "parked-packet-limit", Element::integer },
+ { "allocator", Element::string },
+ { "pd-allocator", Element::string },
+ { "ddns-ttl-percent", Element::real },
+};
+
+/// @brief This table defines default global values for DHCPv6
+///
+/// Some of the global parameters defined in the global scope (i.e. directly
+/// in Dhcp6) are optional. If not defined, the following values will be
+/// used.
+const SimpleDefaults SimpleParser6::GLOBAL6_DEFAULTS = {
+ // preferred-lifetime is unspecified and defaults to 0.625 of valid-lifetime.
+ { "valid-lifetime", Element::integer, "7200" },
+ { "decline-probation-period", Element::integer, "86400" }, // 24h
+ { "dhcp4o6-port", Element::integer, "0" },
+ { "server-tag", Element::string, "" },
+ { "reservations-global", Element::boolean, "false" },
+ { "reservations-in-subnet", Element::boolean, "true" },
+ { "reservations-out-of-pool", Element::boolean, "false" },
+ { "calculate-tee-times", Element::boolean, "true" },
+ { "t1-percent", Element::real, ".50" },
+ { "t2-percent", Element::real, ".80" },
+ { "ddns-send-updates", Element::boolean, "true" },
+ { "ddns-override-no-update", Element::boolean, "false" },
+ { "ddns-override-client-update", Element::boolean, "false" },
+ { "ddns-replace-client-name", Element::string, "never" },
+ { "ddns-generated-prefix", Element::string, "myhost" },
+ { "ddns-qualifying-suffix", Element::string, "" },
+ { "hostname-char-set", Element::string, "[^A-Za-z0-9.-]" },
+ { "hostname-char-replacement", Element::string, "" },
+ { "store-extended-info", Element::boolean, "false" },
+ { "statistic-default-sample-count", Element::integer, "20" },
+ { "statistic-default-sample-age", Element::integer, "0" },
+ { "early-global-reservations-lookup", Element::boolean, "false" },
+ { "ip-reservations-unique", Element::boolean, "true" },
+ { "reservations-lookup-first", Element::boolean, "false" },
+ { "ddns-update-on-renew", Element::boolean, "false" },
+ { "ddns-use-conflict-resolution", Element::boolean, "true" },
+ { "parked-packet-limit", Element::integer, "256" },
+ { "allocator", Element::string, "iterative" },
+ { "pd-allocator", Element::string, "iterative" },
+};
+
+/// @brief This table defines all option definition parameters.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows option_def_param rules in bison grammar.
+const SimpleKeywords SimpleParser6::OPTION6_DEF_PARAMETERS = {
+ { "name", Element::string },
+ { "code", Element::integer },
+ { "type", Element::string },
+ { "record-types", Element::string },
+ { "space", Element::string },
+ { "encapsulate", Element::string },
+ { "array", Element::boolean, },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "metadata", Element::map }
+};
+
+/// @brief This table defines default values for option definitions in DHCPv6.
+///
+/// Dhcp6 may contain an array called option-def that enumerates new option
+/// definitions. This array lists default values for those option definitions.
+const SimpleDefaults SimpleParser6::OPTION6_DEF_DEFAULTS = {
+ { "record-types", Element::string, ""},
+ { "space", Element::string, "dhcp6"}, // DHCP6_OPTION_SPACE
+ { "array", Element::boolean, "false"},
+ { "encapsulate", Element::string, "" }
+};
+
+/// @brief This table defines all option parameters.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows option_param rules in bison grammar.
+const SimpleKeywords SimpleParser6::OPTION6_PARAMETERS = {
+ { "name", Element::string },
+ { "data", Element::string },
+ { "code", Element::integer },
+ { "space", Element::string },
+ { "csv-format", Element::boolean },
+ { "always-send", Element::boolean },
+ { "never-send", Element::boolean },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "metadata", Element::map }
+};
+
+/// @brief This table defines default values for options in DHCPv6.
+///
+/// Dhcp6 usually contains option values (option-data) defined in global,
+/// subnet, class or host reservations scopes. This array lists default values
+/// for those option-data declarations.
+const SimpleDefaults SimpleParser6::OPTION6_DEFAULTS = {
+ { "space", Element::string, "dhcp6"}, // DHCP6_OPTION_SPACE
+ { "csv-format", Element::boolean, "true"},
+ { "always-send", Element::boolean, "false"},
+ { "never-send", Element::boolean, "false"}
+};
+
+/// @brief This table defines all subnet parameters for DHCPv6.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows subnet6_param rule in bison grammar.
+const SimpleKeywords SimpleParser6::SUBNET6_PARAMETERS = {
+ { "preferred-lifetime", Element::integer },
+ { "min-preferred-lifetime", Element::integer },
+ { "max-preferred-lifetime", Element::integer },
+ { "valid-lifetime", Element::integer },
+ { "min-valid-lifetime", Element::integer },
+ { "max-valid-lifetime", Element::integer },
+ { "renew-timer", Element::integer },
+ { "rebind-timer", Element::integer },
+ { "option-data", Element::list },
+ { "pools", Element::list },
+ { "pd-pools", Element::list },
+ { "subnet", Element::string },
+ { "interface", Element::string },
+ { "interface-id", Element::string },
+ { "id", Element::integer },
+ { "rapid-commit", Element::boolean },
+ { "client-class", Element::string },
+ { "require-client-classes", Element::list },
+ { "reservations", Element::list },
+ { "reservation-mode", Element::string },
+ { "reservations-global", Element::boolean },
+ { "reservations-in-subnet", Element::boolean },
+ { "reservations-out-of-pool", Element::boolean },
+ { "relay", Element::map },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "calculate-tee-times", Element::boolean },
+ { "t1-percent", Element::real },
+ { "t2-percent", Element::real },
+ { "ddns-send-updates", Element::boolean },
+ { "ddns-override-no-update", Element::boolean },
+ { "ddns-override-client-update", Element::boolean },
+ { "ddns-replace-client-name", Element::string },
+ { "ddns-generated-prefix", Element::string },
+ { "ddns-qualifying-suffix", Element::string },
+ { "hostname-char-set", Element::string },
+ { "hostname-char-replacement", Element::string },
+ { "store-extended-info", Element::boolean },
+ { "metadata", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean },
+ { "allocator", Element::string },
+ { "pd-allocator", Element::string },
+ { "ddns-ttl-percent", Element::real },
+};
+
+/// @brief This table defines default values for each IPv6 subnet.
+///
+/// Note: When updating this array, please also update SHARED_SUBNET6_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.
+const SimpleDefaults SimpleParser6::SUBNET6_DEFAULTS = {
+ { "id", Element::integer, "0" }, // 0 means autogenerate
+ { "interface", Element::string, "" },
+ { "client-class", Element::string, "" },
+ { "rapid-commit", Element::boolean, "false" }, // rapid-commit disabled by default
+ { "interface-id", Element::string, "" }
+};
+
+/// @brief This table defines default values for each IPv6 subnet that is
+/// part of a shared network
+///
+/// This is mostly the same as @ref SUBNET6_DEFAULTS, except the parameters
+/// that can be derived from shared-network, but cannot from global scope.
+const SimpleDefaults SimpleParser6::SHARED_SUBNET6_DEFAULTS = {
+ { "id", Element::integer, "0" } // 0 means autogenerate
+};
+
+/// @brief This table defines default values for each IPv6 shared network.
+const SimpleDefaults SimpleParser6::SHARED_NETWORK6_DEFAULTS = {
+ { "client-class", Element::string, "" },
+ { "interface", Element::string, "" },
+ { "interface-id", Element::string, "" },
+ { "rapid-commit", Element::boolean, "false" } // rapid-commit disabled by default
+};
+
+/// @brief List of parameters that can be inherited from the global to subnet6 scope.
+///
+/// Some parameters may be defined on both global (directly in Dhcp6) and
+/// 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.
+///
+/// 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 = {
+ "preferred-lifetime",
+ "min-preferred-lifetime",
+ "max-preferred-lifetime",
+ "rebind-timer",
+ "relay",
+ "renew-timer",
+ "valid-lifetime",
+ "min-valid-lifetime",
+ "max-valid-lifetime",
+ "calculate-tee-times",
+ "t1-percent",
+ "t2-percent",
+ "store-extended-info",
+ "cache-threshold",
+ "cache-max-age",
+ "allocator",
+ "pd-allocator"
+};
+
+/// @brief This table defines all pool parameters.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows pool_param rules in bison grammar.
+const SimpleKeywords SimpleParser6::POOL6_PARAMETERS = {
+ { "pool", Element::string },
+ { "pool-id", Element::integer },
+ { "option-data", Element::list },
+ { "client-class", Element::string },
+ { "require-client-classes", Element::list },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "metadata", Element::map }
+};
+
+/// @brief This table defines all prefix delegation pool parameters.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows pd_pool_param rules in bison grammar.
+const SimpleKeywords SimpleParser6::PD_POOL6_PARAMETERS = {
+ { "prefix", Element::string },
+ { "prefix-len", Element::integer },
+ { "delegated-len", Element::integer },
+ { "pool-id", Element::integer },
+ { "option-data", Element::list },
+ { "client-class", Element::string },
+ { "require-client-classes", Element::list },
+ { "excluded-prefix", Element::string },
+ { "excluded-prefix-len", Element::integer },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "metadata", Element::map }
+};
+
+/// @brief This table defines all shared network parameters for DHCPv6.
+///
+/// Boolean, integer, real and string types are for scalar parameters,
+/// list and map types for entries.
+/// Order follows shared_network_param rule in bison grammar.
+const SimpleKeywords SimpleParser6::SHARED_NETWORK6_PARAMETERS = {
+ { "name", Element::string },
+ { "subnet6", Element::list },
+ { "interface", Element::string },
+ { "interface-id", Element::string },
+ { "renew-timer", Element::integer },
+ { "rebind-timer", Element::integer },
+ { "option-data", Element::list },
+ { "relay", Element::map },
+ { "reservation-mode", Element::string },
+ { "reservations-global", Element::boolean },
+ { "reservations-in-subnet", Element::boolean },
+ { "reservations-out-of-pool", Element::boolean },
+ { "client-class", Element::string },
+ { "require-client-classes", Element::list },
+ { "preferred-lifetime", Element::integer },
+ { "min-preferred-lifetime", Element::integer },
+ { "max-preferred-lifetime", Element::integer },
+ { "rapid-commit", Element::boolean },
+ { "valid-lifetime", Element::integer },
+ { "min-valid-lifetime", Element::integer },
+ { "max-valid-lifetime", Element::integer },
+ { "user-context", Element::map },
+ { "comment", Element::string },
+ { "calculate-tee-times", Element::boolean },
+ { "t1-percent", Element::real },
+ { "t2-percent", Element::real },
+ { "ddns-send-updates", Element::boolean },
+ { "ddns-override-no-update", Element::boolean },
+ { "ddns-override-client-update", Element::boolean },
+ { "ddns-replace-client-name", Element::string },
+ { "ddns-generated-prefix", Element::string },
+ { "ddns-qualifying-suffix", Element::string },
+ { "hostname-char-set", Element::string },
+ { "hostname-char-replacement", Element::string },
+ { "store-extended-info", Element::boolean },
+ { "metadata", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean },
+ { "allocator", Element::string },
+ { "pd-allocator", Element::string },
+ { "ddns-ttl-percent", Element::real },
+};
+
+/// @brief This table defines default values for interfaces for DHCPv6.
+const SimpleDefaults SimpleParser6::IFACE6_DEFAULTS = {
+ { "re-detect", Element::boolean, "true" }
+};
+
+/// @brief This table defines default values for dhcp-queue-control in DHCPv6.
+const SimpleDefaults SimpleParser6::DHCP_QUEUE_CONTROL6_DEFAULTS = {
+ { "enable-queue", Element::boolean, "false"},
+ { "queue-type", Element::string, "kea-ring6"},
+ { "capacity", Element::integer, "64"}
+};
+
+/// @brief This table defines default values for multi-threading in DHCPv6.
+const SimpleDefaults SimpleParser6::DHCP_MULTI_THREADING6_DEFAULTS = {
+ { "enable-multi-threading", Element::boolean, "true" },
+ { "thread-pool-size", Element::integer, "0" },
+ { "packet-queue-size", Element::integer, "64" }
+};
+
+/// @brief This defines default values for sanity checking for DHCPv6.
+const SimpleDefaults SimpleParser6::SANITY_CHECKS6_DEFAULTS = {
+ { "lease-checks", Element::string, "warn" }
+};
+
+/// @}
+
+/// ---------------------------------------------------------------------------
+/// --- end of default values -------------------------------------------------
+/// ---------------------------------------------------------------------------
+
+size_t SimpleParser6::setAllDefaults(ElementPtr global) {
+ size_t cnt = 0;
+
+ // Set global defaults first.
+ cnt = setDefaults(global, GLOBAL6_DEFAULTS);
+
+ // Now set the defaults for each specified option definition
+ ConstElementPtr option_defs = global->get("option-def");
+ if (option_defs) {
+ BOOST_FOREACH(ElementPtr option_def, option_defs->listValue()) {
+ cnt += SimpleParser::setDefaults(option_def, OPTION6_DEF_DEFAULTS);
+ }
+ }
+
+ // Set the defaults for option data
+ ConstElementPtr options = global->get("option-data");
+ if (options) {
+ BOOST_FOREACH(ElementPtr single_option, options->listValue()) {
+ cnt += SimpleParser::setDefaults(single_option, OPTION6_DEFAULTS);
+ }
+ }
+
+ // Now set the defaults for defined subnets
+ ConstElementPtr subnets = global->get("subnet6");
+ if (subnets) {
+ cnt += setListDefaults(subnets, SUBNET6_DEFAULTS);
+ }
+
+ // Set the defaults for interfaces config
+ ConstElementPtr ifaces_cfg = global->get("interfaces-config");
+ if (ifaces_cfg) {
+ ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(ifaces_cfg);
+ cnt += setDefaults(mutable_cfg, IFACE6_DEFAULTS);
+ }
+
+ // Set defaults for shared networks
+ 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, SHARED_SUBNET6_DEFAULTS);
+ }
+ }
+ }
+
+ // Set the defaults for dhcp-queue-control. If the element isn't there
+ // we'll add it.
+ ConstElementPtr queue_control = global->get("dhcp-queue-control");
+ ElementPtr mutable_cfg;
+ if (queue_control) {
+ mutable_cfg = boost::const_pointer_cast<Element>(queue_control);
+ } else {
+ mutable_cfg = Element::createMap();
+ global->set("dhcp-queue-control", mutable_cfg);
+ }
+
+ cnt += setDefaults(mutable_cfg, DHCP_QUEUE_CONTROL6_DEFAULTS);
+
+ // Set the defaults for multi-threading. If the element isn't there
+ // we'll add it.
+ ConstElementPtr multi_threading = global->get("multi-threading");
+ if (multi_threading) {
+ mutable_cfg = boost::const_pointer_cast<Element>(multi_threading);
+ } else {
+ mutable_cfg = Element::createMap();
+ global->set("multi-threading", mutable_cfg);
+ }
+
+ cnt += setDefaults(mutable_cfg, DHCP_MULTI_THREADING6_DEFAULTS);
+
+ // Set the defaults for sanity-checks. If the element isn't
+ // there we'll add it.
+ ConstElementPtr sanity_checks = global->get("sanity-checks");
+ if (sanity_checks) {
+ mutable_cfg = boost::const_pointer_cast<Element>(sanity_checks);
+ } else {
+ mutable_cfg = Element::createMap();
+ global->set("sanity-checks", mutable_cfg);
+ }
+
+ cnt += setDefaults(mutable_cfg, SANITY_CHECKS6_DEFAULTS);
+
+ return (cnt);
+}
+
+size_t SimpleParser6::deriveParameters(ElementPtr global) {
+ size_t cnt = 0;
+
+ // Now derive global parameters into subnets.
+ ConstElementPtr subnets = global->get("subnet6");
+ if (subnets) {
+ BOOST_FOREACH(ElementPtr single_subnet, subnets->listValue()) {
+ cnt += SimpleParser::deriveParams(global, single_subnet,
+ INHERIT_TO_SUBNET6);
+ }
+ }
+
+ // Deriving parameters for shared networks is a bit more involved.
+ // First, the shared-network level derives from global, and then
+ // subnets within derive from it.
+ ConstElementPtr shared = global->get("shared-networks");
+ if (shared) {
+ BOOST_FOREACH(ElementPtr net, shared->listValue()) {
+ // First try to inherit the parameters from shared network,
+ // if defined there.
+ // Then try to inherit them from global.
+ cnt += SimpleParser::deriveParams(global, net,
+ 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_TO_SUBNET6);
+ }
+ }
+ }
+ }
+
+ return (cnt);
+}
+
+} // namespace dhcp
+} // namespace isc
diff --git a/src/lib/dhcpsrv/parsers/simple_parser6.h b/src/lib/dhcpsrv/parsers/simple_parser6.h
new file mode 100644
index 0000000..8eae3bf
--- /dev/null
+++ b/src/lib/dhcpsrv/parsers/simple_parser6.h
@@ -0,0 +1,70 @@
+// 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef SIMPLE_PARSER6_H
+#define SIMPLE_PARSER6_H
+
+#include <cc/simple_parser.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief SimpleParser specialized for DHCPv6
+///
+/// This class is a @ref isc::data::SimpleParser dedicated to DHCPv6 parser.
+/// In particular, it contains all the default values and names of the
+/// parameters that are to be derived (inherited) between scopes.
+/// For the actual values, see @file simple_parser6.cc
+class SimpleParser6 : public isc::data::SimpleParser {
+public:
+
+ /// @brief Sets all defaults for DHCPv6 configuration
+ ///
+ /// This method sets global, option data and option definitions defaults.
+ ///
+ /// @param global scope to be filled in with defaults.
+ /// @return number of default values added
+ static size_t setAllDefaults(isc::data::ElementPtr global);
+
+ /// @brief Derives (inherits) all parameters from global to more specific scopes.
+ ///
+ /// This method currently does the following:
+ /// - derives global parameters to subnets (lifetimes for now)
+ /// @param global scope to be modified if needed (subnet6 will be extracted)
+ /// @return number of default values derived
+ static size_t deriveParameters(isc::data::ElementPtr global);
+
+ // see simple_parser6.cc for comments for those parameters
+ static const isc::data::SimpleKeywords GLOBAL6_PARAMETERS;
+ static const isc::data::SimpleDefaults GLOBAL6_DEFAULTS;
+
+ static const isc::data::SimpleKeywords OPTION6_DEF_PARAMETERS;
+ static const isc::data::SimpleDefaults OPTION6_DEF_DEFAULTS;
+
+ static const isc::data::SimpleKeywords OPTION6_PARAMETERS;
+ static const isc::data::SimpleDefaults OPTION6_DEFAULTS;
+
+ static const isc::data::SimpleKeywords SUBNET6_PARAMETERS;
+ static const isc::data::SimpleDefaults SUBNET6_DEFAULTS;
+ static const isc::data::SimpleDefaults SHARED_SUBNET6_DEFAULTS;
+ static const isc::data::ParamsList INHERIT_TO_SUBNET6;
+
+ static const isc::data::SimpleKeywords POOL6_PARAMETERS;
+ static const isc::data::SimpleKeywords PD_POOL6_PARAMETERS;
+
+ static const isc::data::SimpleKeywords SHARED_NETWORK6_PARAMETERS;
+ static const isc::data::SimpleDefaults SHARED_NETWORK6_DEFAULTS;
+
+ static const isc::data::SimpleDefaults IFACE6_DEFAULTS;
+ static const isc::data::SimpleDefaults DHCP_QUEUE_CONTROL6_DEFAULTS;
+ static const isc::data::SimpleDefaults DHCP_MULTI_THREADING6_DEFAULTS;
+ static const isc::data::SimpleDefaults SANITY_CHECKS6_DEFAULTS;
+};
+
+} // namespace dhcp
+} // namespace isc
+
+#endif