summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/host_mgr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/host_mgr.cc')
-rw-r--r--src/lib/dhcpsrv/host_mgr.cc678
1 files changed, 678 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/host_mgr.cc b/src/lib/dhcpsrv/host_mgr.cc
new file mode 100644
index 0000000..1397c48
--- /dev/null
+++ b/src/lib/dhcpsrv/host_mgr.cc
@@ -0,0 +1,678 @@
+// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <dhcpsrv/cfg_hosts.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/host_mgr.h>
+#include <dhcpsrv/hosts_log.h>
+#include <dhcpsrv/host_data_source_factory.h>
+
+namespace {
+
+/// @brief Convenience function returning a pointer to the hosts configuration.
+///
+/// This function is called by the @c HostMgr methods requiring access to the
+/// host reservations specified in the DHCP server configuration.
+///
+/// @return A pointer to the const hosts reservation configuration.
+isc::dhcp::ConstCfgHostsPtr getCfgHosts() {
+ return (isc::dhcp::CfgMgr::instance().getCurrentCfg()->getCfgHosts());
+}
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace dhcp {
+
+using namespace isc::asiolink;
+using namespace isc::db;
+
+IOServicePtr HostMgr::io_service_ = IOServicePtr();
+
+boost::scoped_ptr<HostMgr>&
+HostMgr::getHostMgrPtr() {
+ static boost::scoped_ptr<HostMgr> host_mgr_ptr;
+ return (host_mgr_ptr);
+}
+
+void
+HostMgr::create() {
+ getHostMgrPtr().reset(new HostMgr());
+}
+
+void
+HostMgr::addBackend(const std::string& access) {
+ HostDataSourceFactory::add(getHostMgrPtr()->alternate_sources_, access);
+}
+
+bool
+HostMgr::delBackend(const std::string& db_type) {
+ if (getHostMgrPtr()->cache_ptr_ &&
+ getHostMgrPtr()->cache_ptr_->getType() == db_type) {
+ getHostMgrPtr()->cache_ptr_.reset();
+ }
+ return (HostDataSourceFactory::del(getHostMgrPtr()->alternate_sources_,
+ db_type));
+}
+
+bool
+HostMgr::delBackend(const std::string& db_type, const std::string& access,
+ bool if_unusable) {
+ return (HostDataSourceFactory::del(getHostMgrPtr()->alternate_sources_,
+ db_type, access, if_unusable));
+}
+
+void
+HostMgr::delAllBackends() {
+ getHostMgrPtr()->alternate_sources_.clear();
+}
+
+HostDataSourcePtr
+HostMgr::getHostDataSource() const {
+ if (alternate_sources_.empty()) {
+ return (HostDataSourcePtr());
+ }
+ return (alternate_sources_[0]);
+}
+
+bool
+HostMgr::checkCacheBackend(bool logging) {
+ if (getHostMgrPtr()->cache_ptr_) {
+ return (true);
+ }
+ HostDataSourceList& sources = getHostMgrPtr()->alternate_sources_;
+ if (sources.empty()) {
+ return (false);
+ }
+ CacheHostDataSourcePtr cache_ptr =
+ boost::dynamic_pointer_cast<CacheHostDataSource>(sources[0]);
+ if (cache_ptr) {
+ getHostMgrPtr()->cache_ptr_ = cache_ptr;
+ if (logging) {
+ LOG_INFO(hosts_logger, HOSTS_CFG_CACHE_HOST_DATA_SOURCE)
+ .arg(cache_ptr->getType());
+ }
+ return (true);
+ }
+ return (false);
+}
+
+HostMgr&
+HostMgr::instance() {
+ boost::scoped_ptr<HostMgr>& host_mgr_ptr = getHostMgrPtr();
+ if (!host_mgr_ptr) {
+ create();
+ }
+ return (*host_mgr_ptr);
+}
+
+ConstHostCollection
+HostMgr::getAll(const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ ConstHostCollection hosts = getCfgHosts()->getAll(identifier_type,
+ identifier_begin,
+ identifier_len);
+ for (auto source : alternate_sources_) {
+ ConstHostCollection hosts_plus =
+ source->getAll(identifier_type, identifier_begin, identifier_len);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+ConstHostCollection
+HostMgr::getAll4(const SubnetID& subnet_id) const {
+ ConstHostCollection hosts = getCfgHosts()->getAll4(subnet_id);
+ for (auto source : alternate_sources_) {
+ ConstHostCollection hosts_plus = source->getAll4(subnet_id);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+
+ConstHostCollection
+HostMgr::getAll6(const SubnetID& subnet_id) const {
+ ConstHostCollection hosts = getCfgHosts()->getAll6(subnet_id);
+ for (auto source : alternate_sources_) {
+ ConstHostCollection hosts_plus = source->getAll6(subnet_id);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+ConstHostCollection
+HostMgr::getAllbyHostname(const std::string& hostname) const {
+ ConstHostCollection hosts = getCfgHosts()->getAllbyHostname(hostname);
+ for (auto source : alternate_sources_) {
+ ConstHostCollection hosts_plus = source->getAllbyHostname(hostname);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+ConstHostCollection
+HostMgr::getAllbyHostname4(const std::string& hostname,
+ const SubnetID& subnet_id) const {
+ ConstHostCollection hosts = getCfgHosts()->getAllbyHostname4(hostname,
+ subnet_id);
+ for (auto source : alternate_sources_) {
+ ConstHostCollection hosts_plus = source->getAllbyHostname4(hostname,
+ subnet_id);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+ConstHostCollection
+HostMgr::getAllbyHostname6(const std::string& hostname,
+ const SubnetID& subnet_id) const {
+ ConstHostCollection hosts = getCfgHosts()->getAllbyHostname6(hostname,
+ subnet_id);
+ for (auto source : alternate_sources_) {
+ ConstHostCollection hosts_plus = source->getAllbyHostname6(hostname,
+ subnet_id);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+ConstHostCollection
+HostMgr::getPage4(const SubnetID& subnet_id,
+ size_t& source_index,
+ uint64_t lower_host_id,
+ const HostPageSize& page_size) const {
+ // Return empty if (and only if) sources are exhausted.
+ if (source_index > alternate_sources_.size()) {
+ return (ConstHostCollection());
+ }
+
+ ConstHostCollection hosts;
+ // Source index 0 means config file.
+ if (source_index == 0) {
+ hosts = getCfgHosts()->
+ getPage4(subnet_id, source_index, lower_host_id, page_size);
+ } else {
+ hosts = alternate_sources_[source_index - 1]->
+ getPage4(subnet_id, source_index, lower_host_id, page_size);
+ }
+
+ // When got something return it.
+ if (!hosts.empty()) {
+ return (hosts);
+ }
+
+ // Nothing from this source: try the next one.
+ // Note the recursion is limited to the number of sources in all cases.
+ ++source_index;
+ return (getPage4(subnet_id, source_index, 0UL, page_size));
+}
+
+ConstHostCollection
+HostMgr::getPage6(const SubnetID& subnet_id,
+ size_t& source_index,
+ uint64_t lower_host_id,
+ const HostPageSize& page_size) const {
+ // Return empty if (and only if) sources are exhausted.
+ if (source_index > alternate_sources_.size()) {
+ return (ConstHostCollection());
+ }
+
+ ConstHostCollection hosts;
+ // Source index 0 means config file.
+ if (source_index == 0) {
+ hosts = getCfgHosts()->
+ getPage6(subnet_id, source_index, lower_host_id, page_size);
+ } else {
+ hosts = alternate_sources_[source_index - 1]->
+ getPage6(subnet_id, source_index, lower_host_id, page_size);
+ }
+
+ // When got something return it.
+ if (!hosts.empty()) {
+ return (hosts);
+ }
+
+ // Nothing from this source: try the next one.
+ // Note the recursion is limited to the number of sources in all cases.
+ ++source_index;
+ return (getPage6(subnet_id, source_index, 0UL, page_size));
+}
+
+ConstHostCollection
+HostMgr::getPage4(size_t& source_index,
+ uint64_t lower_host_id,
+ const HostPageSize& page_size) const {
+ // Return empty if (and only if) sources are exhausted.
+ if (source_index > alternate_sources_.size()) {
+ return (ConstHostCollection());
+ }
+
+ ConstHostCollection hosts;
+ // Source index 0 means config file.
+ if (source_index == 0) {
+ hosts = getCfgHosts()->
+ getPage4(source_index, lower_host_id, page_size);
+ } else {
+ hosts = alternate_sources_[source_index - 1]->
+ getPage4(source_index, lower_host_id, page_size);
+ }
+
+ // When got something return it.
+ if (!hosts.empty()) {
+ return (hosts);
+ }
+
+ // Nothing from this source: try the next one.
+ // Note the recursion is limited to the number of sources in all cases.
+ ++source_index;
+ return (getPage4(source_index, 0UL, page_size));
+}
+
+ConstHostCollection
+HostMgr::getPage6(size_t& source_index,
+ uint64_t lower_host_id,
+ const HostPageSize& page_size) const {
+ // Return empty if (and only if) sources are exhausted.
+ if (source_index > alternate_sources_.size()) {
+ return (ConstHostCollection());
+ }
+
+ ConstHostCollection hosts;
+ // Source index 0 means config file.
+ if (source_index == 0) {
+ hosts = getCfgHosts()->
+ getPage6(source_index, lower_host_id, page_size);
+ } else {
+ hosts = alternate_sources_[source_index - 1]->
+ getPage6(source_index, lower_host_id, page_size);
+ }
+
+ // When got something return it.
+ if (!hosts.empty()) {
+ return (hosts);
+ }
+
+ // Nothing from this source: try the next one.
+ // Note the recursion is limited to the number of sources in all cases.
+ ++source_index;
+ return (getPage6(source_index, 0UL, page_size));
+}
+
+ConstHostCollection
+HostMgr::getAll4(const IOAddress& address) const {
+ ConstHostCollection hosts = getCfgHosts()->getAll4(address);
+ for (auto source : alternate_sources_) {
+ ConstHostCollection hosts_plus = source->getAll4(address);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+ConstHostPtr
+HostMgr::get4Any(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ ConstHostPtr host = getCfgHosts()->get4(subnet_id, identifier_type,
+ identifier_begin, identifier_len);
+
+ // Found it in the config file or there are no backends configured?
+ // Then we're done here.
+ if (host || alternate_sources_.empty()) {
+ return (host);
+ }
+
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+ HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_IDENTIFIER)
+ .arg(subnet_id)
+ .arg(Host::getIdentifierAsText(identifier_type, identifier_begin,
+ identifier_len));
+
+ // Try to find a host in each configured backend. We return as soon
+ // as we find first hit.
+ for (auto source : alternate_sources_) {
+ host = source->get4(subnet_id, identifier_type,
+ identifier_begin, identifier_len);
+
+ if (host) {
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+ HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_IDENTIFIER_HOST)
+ .arg(subnet_id)
+ .arg(Host::getIdentifierAsText(identifier_type,
+ identifier_begin,
+ identifier_len))
+ .arg(source->getType())
+ .arg(host->toText());
+
+ if (source != cache_ptr_) {
+ cache(host);
+ }
+ return (host);
+ }
+ }
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+ HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_IDENTIFIER_NULL)
+ .arg(subnet_id)
+ .arg(Host::getIdentifierAsText(identifier_type, identifier_begin,
+ identifier_len));
+ return (ConstHostPtr());
+}
+
+ConstHostPtr
+HostMgr::get4(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ ConstHostPtr host = get4Any(subnet_id, identifier_type,
+ identifier_begin, identifier_len);
+ if (host && host->getNegative()) {
+ return (ConstHostPtr());
+ } else if (!host && negative_caching_) {
+ cacheNegative(subnet_id, SubnetID(SUBNET_ID_UNUSED),
+ identifier_type, identifier_begin, identifier_len);
+ }
+ return (host);
+}
+
+ConstHostPtr
+HostMgr::get4(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const {
+ ConstHostPtr host = getCfgHosts()->get4(subnet_id, address);
+ if (host || alternate_sources_.empty()) {
+ return (host);
+ }
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+ HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_ADDRESS4)
+ .arg(subnet_id)
+ .arg(address.toText());
+ for (auto source : alternate_sources_) {
+ host = source->get4(subnet_id, address);
+ if (host && host->getNegative()) {
+ return (ConstHostPtr());
+ }
+ if (host && source != cache_ptr_) {
+ cache(host);
+ }
+ if (host) {
+ return (host);
+ }
+ }
+ return (ConstHostPtr());
+}
+
+ConstHostCollection
+HostMgr::getAll4(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const {
+ auto hosts = getCfgHosts()->getAll4(subnet_id, address);
+
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+ HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS4)
+ .arg(subnet_id)
+ .arg(address.toText());
+
+ for (auto source : alternate_sources_) {
+ auto hosts_plus = source->getAll4(subnet_id, address);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+ConstHostPtr
+HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
+ ConstHostPtr host = getCfgHosts()->get6(prefix, prefix_len);
+ if (host || alternate_sources_.empty()) {
+ return (host);
+ }
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_MGR_ALTERNATE_GET6_PREFIX)
+ .arg(prefix.toText())
+ .arg(static_cast<int>(prefix_len));
+ for (auto source : alternate_sources_) {
+ host = source->get6(prefix, prefix_len);
+ if (host && host->getNegative()) {
+ return (ConstHostPtr());
+ }
+ if (host && source != cache_ptr_) {
+ cache(host);
+ }
+ if (host) {
+ return (host);
+ }
+ }
+ return (ConstHostPtr());
+}
+
+ConstHostPtr
+HostMgr::get6Any(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ ConstHostPtr host = getCfgHosts()->get6(subnet_id, identifier_type,
+ identifier_begin, identifier_len);
+ if (host || alternate_sources_.empty()) {
+ return (host);
+ }
+
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+ HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER)
+ .arg(subnet_id)
+ .arg(Host::getIdentifierAsText(identifier_type, identifier_begin,
+ identifier_len));
+
+ for (auto source : alternate_sources_) {
+ host = source->get6(subnet_id, identifier_type,
+ identifier_begin, identifier_len);
+
+ if (host) {
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+ HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_HOST)
+ .arg(subnet_id)
+ .arg(Host::getIdentifierAsText(identifier_type,
+ identifier_begin,
+ identifier_len))
+ .arg(source->getType())
+ .arg(host->toText());
+
+ if (source != cache_ptr_) {
+ cache(host);
+ }
+ return (host);
+ }
+ }
+
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+ HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_NULL)
+ .arg(subnet_id)
+ .arg(Host::getIdentifierAsText(identifier_type, identifier_begin,
+ identifier_len));
+
+ return (ConstHostPtr());
+}
+
+ConstHostPtr
+HostMgr::get6(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ ConstHostPtr host = get6Any(subnet_id, identifier_type,
+ identifier_begin, identifier_len);
+ if (host && host->getNegative()) {
+ return (ConstHostPtr());
+ } else if (!host && negative_caching_) {
+ cacheNegative(SubnetID(SUBNET_ID_UNUSED), subnet_id,
+ identifier_type, identifier_begin, identifier_len);
+ }
+ return (host);
+}
+
+ConstHostPtr
+HostMgr::get6(const SubnetID& subnet_id,
+ const asiolink::IOAddress& addr) const {
+ ConstHostPtr host = getCfgHosts()->get6(subnet_id, addr);
+ if (host || alternate_sources_.empty()) {
+ return (host);
+ }
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+ HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_ADDRESS6)
+ .arg(subnet_id)
+ .arg(addr.toText());
+ for (auto source : alternate_sources_) {
+ host = source->get6(subnet_id, addr);
+ if (host && host->getNegative()) {
+ return (ConstHostPtr());
+ }
+ if (host && source != cache_ptr_) {
+ cache(host);
+ }
+ if (host) {
+ return (host);
+ }
+ }
+ return (ConstHostPtr());
+}
+
+ConstHostCollection
+HostMgr::getAll6(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const {
+ auto hosts = getCfgHosts()->getAll6(subnet_id, address);
+
+ LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+ HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS6)
+ .arg(subnet_id)
+ .arg(address.toText());
+
+ for (auto source : alternate_sources_) {
+ auto hosts_plus = source->getAll6(subnet_id, address);
+ hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+ }
+ return (hosts);
+}
+
+void
+HostMgr::add(const HostPtr& host) {
+ if (alternate_sources_.empty()) {
+ isc_throw(NoHostDataSourceManager, "Unable to add new host because there is "
+ "no hosts-database configured.");
+ }
+ for (auto source : alternate_sources_) {
+ source->add(host);
+ }
+ // If no backend throws the host should be cached.
+ if (cache_ptr_) {
+ cache(host);
+ }
+}
+
+bool
+HostMgr::del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) {
+ if (alternate_sources_.empty()) {
+ isc_throw(NoHostDataSourceManager, "Unable to delete a host because there is "
+ "no hosts-database configured.");
+ }
+
+ for (auto source : alternate_sources_) {
+ if (source->del(subnet_id, addr)) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+bool
+HostMgr::del4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin, const size_t identifier_len) {
+ if (alternate_sources_.empty()) {
+ isc_throw(NoHostDataSourceManager, "Unable to delete a host because there is "
+ "no hosts-database configured.");
+ }
+
+ for (auto source : alternate_sources_) {
+ if (source->del4(subnet_id, identifier_type,
+ identifier_begin, identifier_len)) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+bool
+HostMgr::del6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin, const size_t identifier_len) {
+ if (alternate_sources_.empty()) {
+ isc_throw(NoHostDataSourceManager, "unable to delete a host because there is "
+ "no alternate host data source present");
+ }
+
+ for (auto source : alternate_sources_) {
+ if (source->del6(subnet_id, identifier_type,
+ identifier_begin, identifier_len)) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+void
+HostMgr::cache(ConstHostPtr host) const {
+ if (cache_ptr_) {
+ // Need a real host.
+ if (!host || host->getNegative()) {
+ return;
+ }
+ // Replace any existing value.
+ // Don't check the result as it does not matter?
+ cache_ptr_->insert(host, true);
+ }
+}
+
+void
+HostMgr::cacheNegative(const SubnetID& ipv4_subnet_id,
+ const SubnetID& ipv6_subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ if (cache_ptr_ && negative_caching_) {
+ HostPtr host(new Host(identifier_begin, identifier_len,
+ identifier_type,
+ ipv4_subnet_id, ipv6_subnet_id,
+ IOAddress::IPV4_ZERO_ADDRESS()));
+ host->setNegative(true);
+ // Don't replace any existing value.
+ // nor matter if it fails.
+ cache_ptr_->insert(host, false);
+ }
+}
+
+bool
+HostMgr::setIPReservationsUnique(const bool unique) {
+ // Iterate over the alternate sources first, because they may include those
+ // for which the new setting is not supported.
+ for (auto source : alternate_sources_) {
+ if (!source->setIPReservationsUnique(unique)) {
+ // One of the sources does not support this new mode of operation.
+ // Let's log a warning and back off the changes to the default
+ // setting which should always be supported.
+ ip_reservations_unique_ = true;
+ LOG_WARN(hosts_logger, HOSTS_MGR_NON_UNIQUE_IP_UNSUPPORTED)
+ .arg(source->getType());
+ for (auto source : alternate_sources_) {
+ source->setIPReservationsUnique(true);
+ }
+ return (false);
+ }
+ }
+ // Successfully configured the HostMgr to use the new setting.
+ // Remember this setting so we can return it via the
+ // getIPReservationsUnique.
+ ip_reservations_unique_ = unique;
+ return (true);
+}
+
+
+} // end of isc::dhcp namespace
+} // end of isc namespace