diff options
Diffstat (limited to 'src/lib/yang/adaptor_config.cc')
-rw-r--r-- | src/lib/yang/adaptor_config.cc | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/src/lib/yang/adaptor_config.cc b/src/lib/yang/adaptor_config.cc new file mode 100644 index 0000000..2fff557 --- /dev/null +++ b/src/lib/yang/adaptor_config.cc @@ -0,0 +1,658 @@ +// 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 <yang/adaptor_config.h> + +using namespace std; +using namespace isc::data; +using namespace isc::dhcp; + +namespace { +const string DHCP4_SPACE = "dhcp4"; +const string DHCP6_SPACE = "dhcp6"; +} + +namespace isc { +namespace yang { + +AdaptorConfig::AdaptorConfig() { +} + +AdaptorConfig::~AdaptorConfig() { +} + +bool +AdaptorConfig::subnetsCollectID(ConstElementPtr subnets, SubnetIDSet& set) { + bool have_ids = true; + + if (!subnets || subnets->empty()) { + // There are no subnets defined, so technically there are no ids. + // However, the flag is used to determine whether the code later + // needs to call assignIDs. Since there is no need to assign + // anything, the code returns true here. + return (true); + } + + // If there are subnets defined, let's go over them one by one and + // collect subnet-ids used in them. + for (ConstElementPtr subnet : subnets->listValue()) { + if (!collectID(subnet, set)) { + have_ids = false; + } + } + return (have_ids); +} + +bool +AdaptorConfig::sharedNetworksCollectID(ConstElementPtr networks, + SubnetIDSet& set, + const string& subsel) { + if (!networks || networks->empty()) { + // There are no shared networks defined, so technically there are no + // ids. However, the flag is used to determine whether the code later + // needs to call assignIDs. Since there is no need to assign anything, + // the code returns true here. + return (true); + } + + // This determines if EVERY subnet has subnet-id defined. + bool have_ids = true; + for (size_t i = 0; i < networks->size(); ++i) { + ElementPtr network = networks->getNonConst(i); + ConstElementPtr subnets = network->get(subsel); + if (subnets) { + if (!subnets->empty()) { + // If there are subnets, collect their subnet-ids. If any + // of them doesn't have a subnet-id, return false. + if (!subnetsCollectID(subnets, set)) { + have_ids = false; + } + } else { + // There's empty subnets list, so just remove it. + network->remove(subsel); + } + } + } + return (have_ids); +} + +void +AdaptorConfig::subnetsAssignID(ConstElementPtr subnets, SubnetIDSet& set, + SubnetID& next) { + if (!subnets || subnets->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < subnets->size(); ++i) { + ElementPtr subnet = subnets->getNonConst(i); + assignID(subnet, set, next); + } +} + +void +AdaptorConfig::sharedNetworksAssignID(ConstElementPtr networks, + SubnetIDSet& set, SubnetID& next, + const string& subsel) { + if (!networks || networks->empty()) { + // nothing to do here. + return; + } + + for (ConstElementPtr network : networks->listValue()) { + ConstElementPtr subnets = network->get(subsel); + if (!subnets || subnets->empty()) { + continue; + } + + for (size_t i = 0; i < subnets->size(); ++i) { + ElementPtr subnet = subnets->getNonConst(i); + assignID(subnet, set, next); + } + } +} + +void +AdaptorConfig::sanitizePools(ConstElementPtr pools) { + if (!pools || pools->empty()) { + // nothing to do here. + return; + } + + // Canonize (clean up name, remove extra spaces, add one space where + // needed) every pool on the list. + for (size_t i = 0; i < pools->size(); ++i) { + ElementPtr pool = pools->getNonConst(i); + AdaptorPool::canonizePool(pool); + } +} + +void +AdaptorConfig::sanitizePoolsInSubnets(ConstElementPtr subnets) { + if (!subnets || subnets->empty()) { + // nothing to do here. + return; + } + + for (ConstElementPtr subnet : subnets->listValue()) { + sanitizePools(subnet->get("pools")); + } +} + +void +AdaptorConfig::sanitizePoolsInSharedNetworks(ConstElementPtr networks, + const string& subsel) { + if (!networks || networks->empty()) { + // nothing to do here. + return; + } + + for (ConstElementPtr network : networks->listValue()) { + sanitizePoolsInSubnets(network->get(subsel)); + } +} + +void +AdaptorConfig::sanitizeOptionDefList(ConstElementPtr defs, + const string& space, + OptionCodes& codes) { + if (!defs || defs->empty()) { + // nothing to do here. + return; + } + + // Do sanity checks on every option definition and fill any missing + // fields with default values. + for (size_t i = 0; i < defs->size(); ++i) { + ElementPtr def = defs->getNonConst(i); + checkCode(def); + checkType(def); + setSpace(def, space); + collect(def, codes); + } +} + +void +AdaptorConfig::sanitizeOptionDataList(ConstElementPtr options, + const string& space, + const OptionCodes& codes) { + if (!options || options->empty()) { + // nothing to do here. + return; + } + + // Sanitize option-data. The only missing elements we may possibly + // need to fill are option space and option code. + for (size_t i = 0; i < options->size(); ++i) { + ElementPtr option = options->getNonConst(i); + setSpace(option, space); + setCode(option, codes); + } +} + +void +AdaptorConfig::sanitizeOptionClasses(ConstElementPtr classes, + const string& space, + OptionCodes& codes) { + if (!classes || classes->empty()) { + // nothing to do here. + return; + } + + // For every client class defined... + for (size_t i = 0; i < classes->size(); ++i) { + ElementPtr cclass = classes->getNonConst(i); + + if (space == DHCP4_SPACE) { + ConstElementPtr options = cclass->get("option-def"); + if (options) { + if (!options->empty()) { + // If present, sanitize it. + sanitizeOptionDefList(options, space, codes); + } else { + // If empty, remove it. + cclass->remove("option-def"); + } + } + } + + // also sanitize option data. + ConstElementPtr options = cclass->get("option-data"); + if (options) { + if (!options->empty()) { + // If present, sanitize it. + sanitizeOptionDataList(options, space, codes); + } else { + // If empty, remove it. + cclass->remove("option-data"); + } + } + } +} + +void +AdaptorConfig::sanitizeOptionPools(ConstElementPtr pools, const string& space, + const OptionCodes& codes) { + if (!pools || pools->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < pools->size(); ++i) { + ElementPtr pool = pools->getNonConst(i); + ConstElementPtr options = pool->get("option-data"); + if (options) { + if (!options->empty()) { + sanitizeOptionDataList(options, space, codes); + } else { + pool->remove("option-data"); + } + } + } +} + +void +AdaptorConfig::sanitizeOptionHosts(ConstElementPtr hosts, const string& space, + const OptionCodes& codes) { + if (!hosts || hosts->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < hosts->size(); ++i) { + ElementPtr host = hosts->getNonConst(i); + ConstElementPtr options = host->get("option-data"); + if (options) { + if (!options->empty()) { + sanitizeOptionDataList(options, space, codes); + } else { + host->remove("option-data"); + } + } + } +} + +void +AdaptorConfig::sanitizeOptionSubnets(ConstElementPtr subnets, + const string& space, + const OptionCodes& codes) { + if (!subnets || subnets->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < subnets->size(); ++i) { + ElementPtr subnet = subnets->getNonConst(i); + + // Let's try to sanitize option-data first. + ConstElementPtr options = subnet->get("option-data"); + if (options) { + if (!options->empty()) { + sanitizeOptionDataList(options, space, codes); + } else { + subnet->remove("option-data"); + } + } + + // Then try to sanitize pools. + ConstElementPtr pools = subnet->get("pools"); + if (pools) { + if (!pools->empty()) { + sanitizeOptionPools(pools, space, codes); + } else { + subnet->remove("pools"); + } + } + + // If this is v6, also sanitize pd-pools. + if (space == DHCP6_SPACE) { + ConstElementPtr pools = subnet->get("pd-pools"); + if (pools) { + if (!pools->empty()) { + sanitizeOptionPools(pools, space, codes); + } else { + subnet->remove("pd-pools"); + } + } + } + + // Finally, sanitize host reservations. + ConstElementPtr hosts = subnet->get("reservations"); + if (hosts) { + if (!hosts->empty()) { + sanitizeOptionHosts(hosts, space, codes); + } else { + subnet->remove("reservations"); + } + } + } +} + +void +AdaptorConfig::sanitizeOptionSharedNetworks(ConstElementPtr networks, + const string& space, + const OptionCodes& codes) { + if (!networks || networks->empty()) { + // nothing to do here. + return; + } + + // For every shared network... + for (size_t i = 0; i < networks->size(); ++i) { + ElementPtr network = networks->getNonConst(i); + + // try to sanitize shared network options first. + ConstElementPtr options = network->get("option-data"); + if (options) { + if (!options->empty()) { + sanitizeOptionDataList(options, space, codes); + } else { + network->remove("option-data"); + } + } + string subnet = "subnet"; + if (space == DHCP4_SPACE) { + subnet += "4"; + } else { + subnet += "6"; + } + + // Now try to sanitize subnets. + ConstElementPtr subnets = network->get(subnet); + if (subnets) { + if (!subnets->empty()) { + sanitizeOptionSubnets(subnets, space, codes); + } else { + network->remove(subnet); + } + } + } +} + +void +AdaptorConfig::sanitizeRequireClassesPools(ConstElementPtr pools) { + if (!pools || pools->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < pools->size(); ++i) { + ElementPtr pool = pools->getNonConst(i); + ConstElementPtr requires = pool->get("require-client-classes"); + if (requires && requires->empty()) { + pool->remove("require-client-classes"); + } + } +} + +void +AdaptorConfig::sanitizeRequireClassesSubnets(ConstElementPtr subnets) { + if (!subnets || subnets->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < subnets->size(); ++i) { + ElementPtr subnet = subnets->getNonConst(i); + sanitizeRequireClassesPools(subnet->get("pools")); + sanitizeRequireClassesPools(subnet->get("pd-pools")); + ConstElementPtr requires = subnet->get("require-client-classes"); + if (requires && requires->empty()) { + subnet->remove("require-client-classes"); + } + } +} + +void +AdaptorConfig::requireClassesSharedNetworks(ConstElementPtr networks, + const string& subsel) { + if (!networks || networks->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < networks->size(); ++i) { + ElementPtr network = networks->getNonConst(i); + sanitizeRequireClassesSubnets(network->get(subsel)); + ConstElementPtr requires = network->get("require-client-classes"); + if (requires && requires->empty()) { + network->remove("require-client-classes"); + } + } +} + +void +AdaptorConfig::sanitizeHostList(ConstElementPtr hosts) { + + if (!hosts || hosts->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < hosts->size(); ++i) { + ElementPtr host = hosts->getNonConst(i); + quoteIdentifier(host); + } +} + +void +AdaptorConfig::sanitizeHostSubnets(ConstElementPtr subnets) { + + if (!subnets || subnets->empty()) { + // nothing to do here. + return; + } + + for (ConstElementPtr subnet : subnets->listValue()) { + sanitizeHostList(subnet->get("reservations")); + } +} + +void +AdaptorConfig::SanitizeHostsInSharedNetworks(ConstElementPtr networks, + const string& space) { + if (!networks || networks->empty()) { + // nothing to do here. + return; + } + + for (ConstElementPtr network : networks->listValue()) { + if (space == DHCP4_SPACE) { + sanitizeHostSubnets(network->get("subnet4")); + } else { + sanitizeHostSubnets(network->get("subnet6")); + } + } +} + +void +AdaptorConfig::sanitizeRelaySubnets(ConstElementPtr subnets) { + if (!subnets || subnets->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < subnets->size(); ++i) { + ElementPtr subnet = subnets->getNonConst(i); + updateRelay(subnet); + } +} + +void +AdaptorConfig::sanitizeRelayInSharedNetworks(ConstElementPtr networks, + const string& subsel) { + if (!networks || networks->empty()) { + // nothing to do here. + return; + } + + for (size_t i = 0; i < networks->size(); ++i) { + ElementPtr network = networks->getNonConst(i); + updateRelay(network); + sanitizeRelaySubnets(network->get(subsel)); + } +} + +void +AdaptorConfig::sanitizeDatabase(ConstElementPtr dhcp) { + ConstElementPtr database = dhcp->get("hosts-database"); + if (!database) { + // nothing to do here. + return; + } + + ElementPtr mutable_dhcp = boost::const_pointer_cast<Element>(dhcp); + mutable_dhcp->remove("hosts-database"); + ElementPtr list = Element::createList(); + list->add(boost::const_pointer_cast<Element>(database)); + mutable_dhcp->set("hosts-databases", list); +} + +void +AdaptorConfig::sanitizeRelaySuppliedOptions(ConstElementPtr dhcp) { + ConstElementPtr options = dhcp->get("relay-supplied-options"); + if (!options || !options->empty()) { + // nothing to do here. + return; + } + ElementPtr mutable_dhcp = boost::const_pointer_cast<Element>(dhcp); + mutable_dhcp->remove("relay-supplied-options"); +} + +void +AdaptorConfig::preProcess(ElementPtr dhcp, const string& subsel, + const string& space) { + if (!dhcp) { + isc_throw(BadValue, "preProcess: null DHCP config"); + } + bool have_ids = true; + SubnetIDSet set; + ConstElementPtr subnets = dhcp->get(subsel); + if (subnets) { + if (!subnets->empty()) { + if (!subnetsCollectID(subnets, set)) { + have_ids = false; + } + } else { + dhcp->remove(subsel); + } + } + ConstElementPtr networks = dhcp->get("shared-networks"); + if (networks) { + if (!networks->empty()) { + if (!sharedNetworksCollectID(networks, set, subsel)) { + have_ids = false; + } + } else { + dhcp->remove("shared-networks"); + } + } + + if (!have_ids) { + SubnetID next(1); + subnetsAssignID(subnets, set, next); + sharedNetworksAssignID(networks, set, next, subsel); + } + + OptionCodes codes; + initCodes(codes, space);; + ConstElementPtr defs = dhcp->get("option-def"); + if (defs) { + if (!defs->empty()) { + sanitizeOptionDefList(defs, space, codes); + } else { + dhcp->remove("option-def"); + } + } + ConstElementPtr options = dhcp->get("option-data"); + if (options) { + if (!options->empty()) { + sanitizeOptionDataList(options, space, codes); + } else { + dhcp->remove("option-data"); + } + } + ConstElementPtr classes = dhcp->get("client-classes"); + if (classes) { + if (!classes->empty()) { + sanitizeOptionClasses(classes, space, codes); + } else { + dhcp->remove("client-classes"); + } + } + ConstElementPtr hosts = dhcp->get("reservations"); + if (hosts) { + if (!hosts->empty()) { + sanitizeHostList(hosts); + sanitizeOptionHosts(hosts, space, codes); + } else { + dhcp->remove("reservations"); + } + } + sanitizeOptionSubnets(subnets, space, codes); + sanitizeOptionSharedNetworks(networks, space, codes); + + sanitizePoolsInSubnets(subnets); + sanitizePoolsInSharedNetworks(networks, subsel); + + sanitizeHostSubnets(subnets); + SanitizeHostsInSharedNetworks(networks, space); + + sanitizeRelaySubnets(subnets); + sanitizeRelayInSharedNetworks(networks, subsel); + + sanitizeRequireClassesSubnets(subnets); + requireClassesSharedNetworks(networks, subsel); + + sanitizeDatabase(dhcp); + + if (space == DHCP6_SPACE) { + sanitizeRelaySuppliedOptions(dhcp); + } +} + +void +AdaptorConfig::preProcess4(ConstElementPtr config) { + if (!config) { + isc_throw(BadValue, "preProcess4: null config"); + } + if (config->getType() != Element::map) { + isc_throw(BadValue, "preProcess4: not map: " << config->str()); + } + if (config->contains("Logging")) { + isc_throw(BadValue, "preProcess4: got Logging object"); + } + ConstElementPtr dhcp = config->get("Dhcp4"); + if (!dhcp) { + return; + } + ElementPtr mutable_dhcp = boost::const_pointer_cast<Element>(dhcp); + preProcess(mutable_dhcp, "subnet4", DHCP4_SPACE); +} + +void +AdaptorConfig::preProcess6(ConstElementPtr config) { + if (!config) { + isc_throw(BadValue, "preProcess6: null config"); + } + if (config->getType() != Element::map) { + isc_throw(BadValue, "preProcess6: not map: " << config->str()); + } + if (config->contains("Logging")) { + isc_throw(BadValue, "preProcess6: got Logging object"); + } + ConstElementPtr dhcp = config->get("Dhcp6"); + if (!dhcp) { + return; + } + ElementPtr mutable_dhcp = boost::const_pointer_cast<Element>(dhcp); + preProcess(mutable_dhcp, "subnet6", DHCP6_SPACE); +} + +} // end of namespace isc::yang +} // end of namespace isc |