// 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 #include #include #include #include #include #include #include #include #include #include #include using namespace isc::asiolink; using namespace isc::data; using namespace std; namespace isc { namespace dhcp { ConstHostCollection CfgHosts::getAll(const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) const { // Do not issue logging message here because it will be logged by // the getAllInternal method. ConstHostCollection collection; getAllInternal(identifier_type, identifier_begin, identifier_len, collection); return (collection); } HostCollection CfgHosts::getAll(const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) { // Do not issue logging message here because it will be logged by // the getAllInternal method. HostCollection collection; getAllInternal(identifier_type, identifier_begin, identifier_len, collection); return (collection); } ConstHostCollection CfgHosts::getAll4(const SubnetID& subnet_id) const { // Do not issue logging message here because it will be logged by // the getAllInternal4 method. ConstHostCollection collection; getAllInternal4(subnet_id, collection); return (collection); } HostCollection CfgHosts::getAll4(const SubnetID& subnet_id) { // Do not issue logging message here because it will be logged by // the getAllInternal4 method. HostCollection collection; getAllInternal4(subnet_id, collection); return (collection); } ConstHostCollection CfgHosts::getAll6(const SubnetID& subnet_id) const { // Do not issue logging message here because it will be logged by // the getAllInternal6 method. ConstHostCollection collection; getAllInternal6(subnet_id, collection); return (collection); } HostCollection CfgHosts::getAll6(const SubnetID& subnet_id) { // Do not issue logging message here because it will be logged by // the getAllInternal6 method. HostCollection collection; getAllInternal6(subnet_id, collection); return (collection); } ConstHostCollection CfgHosts::getAllbyHostname(const std::string& hostname) const { // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal method. ConstHostCollection collection; getAllbyHostnameInternal(hostname, collection); return (collection); } HostCollection CfgHosts::getAllbyHostname(const std::string& hostname) { // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal method. HostCollection collection; getAllbyHostnameInternal(hostname, collection); return (collection); } ConstHostCollection CfgHosts::getAllbyHostname4(const std::string& hostname, const SubnetID& subnet_id) const { // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal4 method. ConstHostCollection collection; getAllbyHostnameInternal4(hostname, subnet_id, collection); return (collection); } HostCollection CfgHosts::getAllbyHostname4(const std::string& hostname, const SubnetID& subnet_id) { // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal4 method. HostCollection collection; getAllbyHostnameInternal4(hostname, subnet_id, collection); return (collection); } ConstHostCollection CfgHosts::getAllbyHostname6(const std::string& hostname, const SubnetID& subnet_id) const { // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal6 method. ConstHostCollection collection; getAllbyHostnameInternal6(hostname, subnet_id, collection); return (collection); } HostCollection CfgHosts::getAllbyHostname6(const std::string& hostname, const SubnetID& subnet_id) { // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal6 method. HostCollection collection; getAllbyHostnameInternal6(hostname, subnet_id, collection); return (collection); } ConstHostCollection CfgHosts::getPage4(const SubnetID& subnet_id, size_t& /*source_index*/, uint64_t lower_host_id, const HostPageSize& page_size) const { // Do not issue logging message here because it will be logged by // the getPageInternal4 method. ConstHostCollection collection; getPageInternal4(subnet_id, lower_host_id, page_size, collection); return (collection); } HostCollection CfgHosts::getPage4(const SubnetID& subnet_id, size_t& /*source_index*/, uint64_t lower_host_id, const HostPageSize& page_size) { // Do not issue logging message here because it will be logged by // the getPageInternal4 method. HostCollection collection; getPageInternal4(subnet_id, lower_host_id, page_size, collection); return (collection); } ConstHostCollection CfgHosts::getPage6(const SubnetID& subnet_id, size_t& /*source_index*/, uint64_t lower_host_id, const HostPageSize& page_size) const { // Do not issue logging message here because it will be logged by // the getPageInternal6 method. ConstHostCollection collection; getPageInternal6(subnet_id, lower_host_id, page_size, collection); return (collection); } HostCollection CfgHosts::getPage6(const SubnetID& subnet_id, size_t& /*source_index*/, uint64_t lower_host_id, const HostPageSize& page_size) { // Do not issue logging message here because it will be logged by // the getPageInternal6 method. HostCollection collection; getPageInternal6(subnet_id, lower_host_id, page_size, collection); return (collection); } ConstHostCollection CfgHosts::getPage4(size_t& /*source_index*/, uint64_t lower_host_id, const HostPageSize& page_size) const { // Do not issue logging message here because it will be logged by // the getPageInternal method. ConstHostCollection collection; getPageInternal(lower_host_id, page_size, collection); return (collection); } HostCollection CfgHosts::getPage4(size_t& /*source_index*/, uint64_t lower_host_id, const HostPageSize& page_size) { // Do not issue logging message here because it will be logged by // the getPageInternal method. HostCollection collection; getPageInternal(lower_host_id, page_size, collection); return (collection); } ConstHostCollection CfgHosts::getPage6(size_t& /*source_index*/, uint64_t lower_host_id, const HostPageSize& page_size) const { // Do not issue logging message here because it will be logged by // the getPageInternal method. ConstHostCollection collection; getPageInternal(lower_host_id, page_size, collection); return (collection); } HostCollection CfgHosts::getPage6(size_t& /*source_index*/, uint64_t lower_host_id, const HostPageSize& page_size) { // Do not issue logging message here because it will be logged by // the getPageInternal method. HostCollection collection; getPageInternal(lower_host_id, page_size, collection); return (collection); } ConstHostCollection CfgHosts::getAll4(const IOAddress& address) const { // Do not issue logging message here because it will be logged by // the getAllInternal4 method. ConstHostCollection collection; getAllInternal4(address, collection); return (collection); } HostCollection CfgHosts::getAll4(const IOAddress& address) { // Do not issue logging message here because it will be logged by // the getAllInternal4 method. HostCollection collection; getAllInternal4(address, collection); return (collection); } ConstHostCollection CfgHosts::getAll6(const IOAddress& address) const { // Do not issue logging message here because it will be logged by // the getAllInternal6 method. ConstHostCollection collection; getAllInternal6(address, collection); return (collection); } HostCollection CfgHosts::getAll6(const IOAddress& address) { // Do not issue logging message here because it will be logged by // the getAllInternal6 method. HostCollection collection; getAllInternal6(address, collection); return (collection); } template void CfgHosts::getAllInternal(const Host::IdentifierType& identifier_type, const uint8_t* identifier, const size_t identifier_len, Storage& storage) const { // Convert host identifier into textual format for logging purposes. // This conversion is exception free. std::string identifier_text = Host::getIdentifierAsText(identifier_type, identifier, identifier_len); LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_IDENTIFIER) .arg(identifier_text); // Use the identifier and identifier type as a composite key. const HostContainerIndex0& idx = hosts_.get<0>(); boost::tuple, const Host::IdentifierType> t = boost::make_tuple(std::vector(identifier, identifier + identifier_len), identifier_type); // Append each Host object to the storage. for (HostContainerIndex0::iterator host = idx.lower_bound(t); host != idx.upper_bound(t); ++host) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_IDENTIFIER_HOST) .arg(identifier_text) .arg((*host)->toText()); storage.push_back(*host); } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_IDENTIFIER_COUNT) .arg(identifier_text) .arg(storage.size()); } template void CfgHosts::getAllInternal4(const SubnetID& subnet_id, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID4) .arg(subnet_id); // Use try DHCPv4 subnet id. const HostContainerIndex2& idx = hosts_.get<2>(); // Append each Host object to the storage. for (HostContainerIndex2::iterator host = idx.lower_bound(subnet_id); host != idx.upper_bound(subnet_id); ++host) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_SUBNET_ID4_HOST) .arg(subnet_id) .arg((*host)->toText()); storage.push_back(*host); } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_SUBNET_ID4_COUNT) .arg(subnet_id) .arg(storage.size()); } template void CfgHosts::getAllInternal6(const SubnetID& subnet_id, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID6) .arg(subnet_id); // Use try DHCPv6 subnet id. const HostContainerIndex3& idx = hosts_.get<3>(); // Append each Host object to the storage. for (HostContainerIndex3::iterator host = idx.lower_bound(subnet_id); host != idx.upper_bound(subnet_id); ++host) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_SUBNET_ID6_HOST) .arg(subnet_id) .arg((*host)->toText()); storage.push_back(*host); } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_SUBNET_ID6_COUNT) .arg(subnet_id) .arg(storage.size()); } template void CfgHosts::getAllbyHostnameInternal(const std::string& hostname, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_HOSTNAME) .arg(hostname); // Use try hostname. const HostContainerIndex5& idx = hosts_.get<5>(); // Append each Host object to the storage. for (HostContainerIndex5::iterator host = idx.lower_bound(hostname); host != idx.upper_bound(hostname); ++host) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_HOSTNAME_HOST) .arg(hostname) .arg((*host)->toText()); storage.push_back(*host); } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_HOSTNAME_COUNT) .arg(hostname) .arg(storage.size()); } template void CfgHosts::getAllbyHostnameInternal4(const std::string& hostname, const SubnetID& subnet_id, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_HOSTNAME_SUBNET_ID4) .arg(hostname) .arg(subnet_id); // Use try hostname. const HostContainerIndex5& idx = hosts_.get<5>(); // Append each Host object to the storage. for (HostContainerIndex5::iterator host = idx.lower_bound(hostname); host != idx.upper_bound(hostname); ++host) { if ((*host)->getIPv4SubnetID() != subnet_id) { continue; } LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_HOSTNAME_SUBNET_ID4_HOST) .arg(hostname) .arg(subnet_id) .arg((*host)->toText()); storage.push_back(*host); } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_HOSTNAME_SUBNET_ID4_COUNT) .arg(hostname) .arg(subnet_id) .arg(storage.size()); } template void CfgHosts::getAllbyHostnameInternal6(const std::string& hostname, const SubnetID& subnet_id, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_HOSTNAME_SUBNET_ID6) .arg(hostname) .arg(subnet_id); // Use try hostname. const HostContainerIndex5& idx = hosts_.get<5>(); // Append each Host object to the storage. for (HostContainerIndex5::iterator host = idx.lower_bound(hostname); host != idx.upper_bound(hostname); ++host) { if ((*host)->getIPv6SubnetID() != subnet_id) { continue; } LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_HOSTNAME_SUBNET_ID6_HOST) .arg(hostname) .arg(subnet_id) .arg((*host)->toText()); storage.push_back(*host); } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_HOSTNAME_SUBNET_ID6_COUNT) .arg(hostname) .arg(subnet_id) .arg(storage.size()); } template void CfgHosts::getPageInternal(uint64_t lower_host_id, const HostPageSize& page_size, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL); // Use the host id last index. const HostContainerIndex4& idx = hosts_.get<4>(); HostContainerIndex4::const_iterator host = idx.lower_bound(lower_host_id); // Exclude the lower bound id when it is not zero. if (lower_host_id && (host != idx.end()) && ((*host)->getHostId() == lower_host_id)) { ++host; } // Return hosts within the page size. for (; host != idx.end(); ++host) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_HOST) .arg((*host)->toText()); storage.push_back(*host); if (storage.size() >= page_size.page_size_) { break; } } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_COUNT) .arg(storage.size()); } template void CfgHosts::getPageInternal4(const SubnetID& subnet_id, uint64_t lower_host_id, const HostPageSize& page_size, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID4) .arg(subnet_id); // Use the host id last index. const HostContainerIndex4& idx = hosts_.get<4>(); HostContainerIndex4::const_iterator host = idx.lower_bound(lower_host_id); // Exclude the lower bound id when it is not zero. if (lower_host_id && (host != idx.end()) && ((*host)->getHostId() == lower_host_id)) { ++host; } // Return hosts in the subnet within the page size. for (; host != idx.end(); ++host) { if ((*host)->getIPv4SubnetID() != subnet_id) { continue; } LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_SUBNET_ID4_HOST) .arg(subnet_id) .arg((*host)->toText()); storage.push_back(*host); if (storage.size() >= page_size.page_size_) { break; } } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_SUBNET_ID4_COUNT) .arg(subnet_id) .arg(storage.size()); } template void CfgHosts::getPageInternal6(const SubnetID& subnet_id, uint64_t lower_host_id, const HostPageSize& page_size, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID6) .arg(subnet_id); // Use the host id last index. const HostContainerIndex4& idx = hosts_.get<4>(); HostContainerIndex4::const_iterator host = idx.lower_bound(lower_host_id); // Exclude the lower bound id when it is not zero. if (lower_host_id && (host != idx.end()) && ((*host)->getHostId() == lower_host_id)) { ++host; } // Return hosts in the subnet within the page size. for (; host != idx.end(); ++host) { if ((*host)->getIPv6SubnetID() != subnet_id) { continue; } LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_SUBNET_ID6_HOST) .arg(subnet_id) .arg((*host)->toText()); storage.push_back(*host); if (storage.size() >= page_size.page_size_) { break; } } // Log how many hosts have been found. LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_SUBNET_ID6_COUNT) .arg(subnet_id) .arg(storage.size()); } template void CfgHosts::getAllInternal4(const IOAddress& address, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_ADDRESS4) .arg(address.toText()); // Must not specify address other than IPv4. if (!address.isV4()) { isc_throw(BadHostAddress, "must specify an IPv4 address when searching" " for a host, specified address was " << address); } // Search for the Host using the reserved IPv4 address as a key. const HostContainerIndex1& idx = hosts_.get<1>(); HostContainerIndex1Range r = idx.equal_range(address); // Append each Host object to the storage. for (HostContainerIndex1::iterator host = r.first; host != r.second; ++host) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_ADDRESS4_HOST) .arg(address.toText()) .arg((*host)->toText()); storage.push_back(*host); } LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_ADDRESS4_COUNT) .arg(address.toText()) .arg(storage.size()); } template void CfgHosts::getAllInternal6(const IOAddress& address, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_ADDRESS6) .arg(address.toText()); // Must not specify address other than IPv6. if (!address.isV6()) { isc_throw(BadHostAddress, "must specify an IPv6 address when searching" " for a host, specified address was " << address); } // Search for the Host using the reserved IPv6 address as a key. const HostContainer6Index4& idx = hosts6_.get<4>(); HostContainer6Index4Range r = idx.equal_range(address); // Append each Host object to the storage. for (HostContainer6Index4::iterator reservation = r.first; reservation != r.second; ++reservation) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_ADDRESS6_HOST) .arg(address.toText()) .arg(reservation->host_->toText()); storage.push_back(reservation->host_); } LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_ADDRESS6_COUNT) .arg(address.toText()) .arg(storage.size()); } ConstHostPtr CfgHosts::get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) const { return (getHostInternal(subnet_id, false, identifier_type, identifier_begin, identifier_len)); } HostPtr CfgHosts::get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) { return (getHostInternal(subnet_id, false, identifier_type, identifier_begin, identifier_len)); } ConstHostPtr CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4) .arg(subnet_id).arg(address.toText()); ConstHostCollection hosts = getAll4(address); for (ConstHostCollection::const_iterator host = hosts.begin(); host != hosts.end(); ++host) { if ((*host)->getIPv4SubnetID() == subnet_id) { LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_HOST) .arg(subnet_id) .arg(address.toText()) .arg((*host)->toText()); return (*host); } } LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_NULL) .arg(subnet_id).arg(address.toText()); return (ConstHostPtr()); } ConstHostCollection CfgHosts::getAll4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4) .arg(subnet_id).arg(address.toText()); ConstHostCollection hosts; for (auto host : getAll4(address)) { if (host->getIPv4SubnetID() == subnet_id) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_HOST) .arg(subnet_id) .arg(address.toText()) .arg(host->toText()); hosts.push_back(host); } } LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_COUNT) .arg(subnet_id) .arg(address.toText()) .arg(hosts.size()); return (hosts); } ConstHostPtr CfgHosts::get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) const { return (getHostInternal(subnet_id, true, identifier_type, identifier_begin, identifier_len)); } HostPtr CfgHosts::get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) { return (getHostInternal(subnet_id, true, identifier_type, identifier_begin, identifier_len)); } ConstHostPtr CfgHosts::get6(const IOAddress& prefix, const uint8_t prefix_len) const { return (getHostInternal6(prefix, prefix_len)); } HostPtr CfgHosts::get6(const IOAddress& prefix, const uint8_t prefix_len) { return (getHostInternal6(prefix, prefix_len)); } ConstHostPtr CfgHosts::get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { // Do not log here because getHostInternal6 logs. return (getHostInternal6(subnet_id, address)); } HostPtr CfgHosts::get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) { // Do not log here because getHostInternal6 logs. return (getHostInternal6(subnet_id, address)); } ConstHostCollection CfgHosts::getAll6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { ConstHostCollection hosts; getAllInternal6(subnet_id, address, hosts); return (hosts); } template ReturnType CfgHosts::getHostInternal6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6) .arg(subnet_id).arg(address.toText()); Storage storage; getAllInternal6(subnet_id, address, storage); switch (storage.size()) { case 0: LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6_NULL) .arg(subnet_id) .arg(address.toText()); return (HostPtr()); case 1: LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6_HOST) .arg(subnet_id) .arg(address.toText()) .arg((*storage.begin())->toText()); return (*storage.begin()); default: isc_throw(DuplicateHost, "more than one reservation found" " for the host belonging to the subnet with id '" << subnet_id << "' and using the address '" << address.toText() << "'"); } } template ReturnType CfgHosts::getHostInternal6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_PREFIX) .arg(prefix.toText()).arg(static_cast(prefix_len)); // Let's get all reservations that match subnet_id, address. const HostContainer6Index0& idx = hosts6_.get<0>(); HostContainer6Index0Range r = make_pair(idx.lower_bound(prefix), idx.upper_bound(prefix)); for (HostContainer6Index0::iterator resrv = r.first; resrv != r.second; ++resrv) { if (resrv->resrv_.getPrefixLen() == prefix_len) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ONE_PREFIX_HOST) .arg(prefix.toText()) .arg(static_cast(prefix_len)) .arg(resrv->host_->toText()); return (resrv->host_); } } LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ONE_PREFIX_NULL) .arg(prefix.toText()) .arg(static_cast(prefix_len)); return (ReturnType()); } template void CfgHosts::getAllInternal6(const SubnetID& subnet_id, const asiolink::IOAddress& address, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6) .arg(subnet_id).arg(address.toText()); // Must not specify address other than IPv6. if (!address.isV6()) { isc_throw(BadHostAddress, "must specify an IPv6 address when searching" " for a host, specified address was " << address); } // Let's get all reservations that match subnet_id, address. const HostContainer6Index1& idx = hosts6_.get<1>(); HostContainer6Index1Range r = make_pair(idx.lower_bound(boost::make_tuple(subnet_id, address)), idx.upper_bound(boost::make_tuple(subnet_id, address))); // For each IPv6 reservation, add the host to the results list. Fortunately, // in all sane cases, there will be only one such host. (Each host can have // multiple addresses reserved, but for each (address, subnet_id) there should // be at most one host reserving it). for(HostContainer6Index1::iterator resrv = r.first; resrv != r.second; ++resrv) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST) .arg(subnet_id) .arg(address.toText()) .arg(resrv->host_->toText()); storage.push_back(resrv->host_); } LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_COUNT) .arg(subnet_id) .arg(address.toText()) .arg(storage.size()); } HostPtr CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6, const Host::IdentifierType& identifier_type, const uint8_t* identifier, const size_t identifier_len) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER) .arg(subnet6 ? "IPv6" : "IPv4") .arg(subnet_id) .arg(Host::getIdentifierAsText(identifier_type, identifier, identifier_len)); // Get all hosts for a specified identifier. This may return multiple hosts // for different subnets, but the number of hosts returned should be low // because one host presumably doesn't show up in many subnets. HostCollection hosts; getAllInternal(identifier_type, identifier, identifier_len, hosts); HostPtr host; // Iterate over the returned hosts and select those for which the // subnet id matches. for (HostCollection::const_iterator host_it = hosts.begin(); host_it != hosts.end(); ++host_it) { // Check if this is IPv4 subnet or IPv6 subnet. SubnetID host_subnet_id = subnet6 ? (*host_it)->getIPv6SubnetID() : (*host_it)->getIPv4SubnetID(); if (subnet_id == host_subnet_id) { // If this is the first occurrence of the host for this subnet, // remember it. But, if we find that this is second @c Host object // for the same client, it is a misconfiguration. Most likely, // the administrator has specified one reservation for a HW // address and another one for the DUID, which gives an ambiguous // result, and we don't know which reservation we should choose. // Therefore, throw an exception. if (!host) { host = *host_it; } else { isc_throw(DuplicateHost, "more than one reservation found" " for the host belonging to the subnet with id '" << subnet_id << "' and using the identifier '" << Host::getIdentifierAsText(identifier_type, identifier, identifier_len) << "'"); } } } if (host) { LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER_HOST) .arg(subnet_id) .arg(Host::getIdentifierAsText(identifier_type, identifier, identifier_len)) .arg(host->toText()); } else { LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER_NULL) .arg(subnet_id) .arg(Host::getIdentifierAsText(identifier_type, identifier, identifier_len)); } return (host); } void CfgHosts::add(const HostPtr& host) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_ADD_HOST) .arg(host ? host->toText() : "(no-host)"); // Sanity check that the host is non-null. if (!host) { isc_throw(BadValue, "specified host object must not be NULL when it" " is added to the configuration"); } // At least one subnet ID must be used. if (host->getIPv4SubnetID() == SUBNET_ID_UNUSED && host->getIPv6SubnetID() == SUBNET_ID_UNUSED) { isc_throw(BadValue, "must not use both IPv4 and IPv6 subnet ids of" " 0 when adding new host reservation"); } add4(host); add6(host); } void CfgHosts::add4(const HostPtr& host) { HWAddrPtr hwaddr = host->getHWAddress(); DuidPtr duid = host->getDuid(); // Check for duplicates for the specified IPv4 subnet. if (host->getIPv4SubnetID() != SUBNET_ID_UNUSED) { if (hwaddr && !hwaddr->hwaddr_.empty() && get4(host->getIPv4SubnetID(), Host::IDENT_HWADDR, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) { isc_throw(DuplicateHost, "failed to add new host using the HW" << " address '" << hwaddr->toText(false) << "' to the IPv4 subnet id '" << host->getIPv4SubnetID() << "' as this host has already been added"); } if (duid && !duid->getDuid().empty() && get4(host->getIPv4SubnetID(), Host::IDENT_DUID, &duid->getDuid()[0], duid->getDuid().size())) { isc_throw(DuplicateHost, "failed to add new host using the " << "DUID '" << duid->toText() << "' to the IPv4 subnet id '" << host->getIPv4SubnetID() << "' as this host has already been added"); } // Check for duplicates for the specified IPv6 subnet. } else if (host->getIPv6SubnetID() != SUBNET_ID_UNUSED) { if (duid && !duid->getDuid().empty() && get6(host->getIPv6SubnetID(), Host::IDENT_DUID, &duid->getDuid()[0], duid->getDuid().size())) { isc_throw(DuplicateHost, "failed to add new host using the " << "DUID '" << duid->toText() << "' to the IPv6 subnet id '" << host->getIPv6SubnetID() << "' as this host has already been added"); } if (hwaddr && !hwaddr->hwaddr_.empty() && get6(host->getIPv6SubnetID(), Host::IDENT_HWADDR, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) { isc_throw(DuplicateHost, "failed to add new host using the HW" << " address '" << hwaddr->toText(false) << "' to the IPv6 subnet id '" << host->getIPv6SubnetID() << "' as this host has already been added"); } } // Check if the address is already reserved for the specified IPv4 subnet. if (ip_reservations_unique_ && !host->getIPv4Reservation().isV4Zero() && (host->getIPv4SubnetID() != SUBNET_ID_UNUSED) && get4(host->getIPv4SubnetID(), host->getIPv4Reservation())) { isc_throw(ReservedAddress, "failed to add new host using the HW" " address '" << (hwaddr ? hwaddr->toText(false) : "(null)") << " and DUID '" << (duid ? duid->toText() : "(null)") << "' to the IPv4 subnet id '" << host->getIPv4SubnetID() << "' for the address " << host->getIPv4Reservation() << ": There's already a reservation for this address"); } // Check if the (identifier type, identifier) tuple is already used. const std::vector& id = host->getIdentifier(); if ((host->getIPv4SubnetID() != SUBNET_ID_UNUSED) && !id.empty()) { if (get4(host->getIPv4SubnetID(), host->getIdentifierType(), &id[0], id.size())) { isc_throw(DuplicateHost, "failed to add duplicate IPv4 host using identifier: " << Host::getIdentifierAsText(host->getIdentifierType(), &id[0], id.size())); } } // This is a new instance - add it. host->setHostId(++next_host_id_); hosts_.insert(host); } void CfgHosts::add6(const HostPtr& host) { if (host->getIPv6SubnetID() == SUBNET_ID_UNUSED) { // This is IPv4-only host. No need to add it to v6 tables. return; } HWAddrPtr hwaddr = host->getHWAddress(); DuidPtr duid = host->getDuid(); // Get all reservations for this host. IPv6ResrvRange reservations = host->getIPv6Reservations(); // Check if there are any IPv6 reservations. if (std::distance(reservations.first, reservations.second) == 0) { // If there aren't, we don't need to add this to hosts6_, which is used // for getting hosts by their IPv6 address reservations. return; } // Now for each reservation, insert corresponding (address, host) tuple. for (IPv6ResrvIterator it = reservations.first; it != reservations.second; ++it) { if (ip_reservations_unique_) { // If there's an entry for this (subnet-id, address), reject it. if (get6(host->getIPv6SubnetID(), it->second.getPrefix())) { isc_throw(DuplicateHost, "failed to add address reservation for " << "host using the HW address '" << (hwaddr ? hwaddr->toText(false) : "(null)") << " and DUID '" << (duid ? duid->toText() : "(null)") << "' to the IPv6 subnet id '" << host->getIPv6SubnetID() << "' for address/prefix " << it->second.getPrefix() << ": There's already reservation for this address/prefix"); } } hosts6_.insert(HostResrv6Tuple(it->second, host)); } } bool CfgHosts::del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) { size_t erased_hosts = 0; size_t erased_addresses = 0; if (addr.isV4()) { HostContainerIndex4& idx = hosts_.get<4>(); // Delete IPv4 reservation and host. for (auto host : getAll4(subnet_id, addr)) { erased_hosts += idx.erase(host->getHostId()); } erased_addresses = erased_hosts; } else { HostContainer6Index1& idx6 = hosts6_.get<1>(); HostContainerIndex4& idx = hosts_.get<4>(); // Delete IPv6 reservations. const auto& range = idx6.equal_range(boost::make_tuple(subnet_id, addr)); erased_addresses = boost::distance(range); // Delete hosts. for (auto key = range.first; key != range.second; ++key) { erased_hosts += idx.erase(key->host_->getHostId()); } idx6.erase(range.first, range.second); } LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL) .arg(erased_hosts) .arg(erased_addresses) .arg(subnet_id) .arg(addr.toText()); return (erased_hosts != 0); } size_t CfgHosts::delAll4(const SubnetID& subnet_id) { HostContainerIndex2& idx = hosts_.get<2>(); size_t erased = idx.erase(subnet_id); LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL_ALL_SUBNET4) .arg(erased) .arg(subnet_id); return (erased); } bool CfgHosts::del4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) { HostContainerIndex0& idx = hosts_.get<0>(); const auto t = boost::make_tuple(std::vector(identifier_begin, identifier_begin + identifier_len), identifier_type); const auto& range = idx.equal_range(t); size_t erased = 0; for (auto key = range.first; key != range.second;) { if ((*key)->getIPv4SubnetID() != subnet_id) { ++key; // Skip hosts from other subnets. continue; } key = idx.erase(key); erased++; } LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL4) .arg(erased) .arg(subnet_id) .arg(Host::getIdentifierAsText(identifier_type, identifier_begin, identifier_len)); return (erased != 0); } size_t CfgHosts::delAll6(const SubnetID& subnet_id) { // Delete IPv6 reservations. HostContainer6Index2& idx6 = hosts6_.get<2>(); size_t erased_addresses = idx6.erase(subnet_id); // Delete hosts. HostContainerIndex3& idx = hosts_.get<3>(); size_t erased_hosts = idx.erase(subnet_id); LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL_ALL_SUBNET6) .arg(erased_hosts) .arg(erased_addresses) .arg(subnet_id); return (erased_hosts); } bool CfgHosts::del6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) { HostContainerIndex0& idx = hosts_.get<0>(); HostContainer6Index3& idx6 = hosts6_.get<3>(); const auto t = boost::make_tuple(std::vector(identifier_begin, identifier_begin + identifier_len), identifier_type); const auto& range = idx.equal_range(t); size_t erased_hosts = 0; size_t erased_reservations = 0; for (auto key = range.first; key != range.second;) { if ((*key)->getIPv6SubnetID() != subnet_id) { ++key; // Skip hosts from other subnets. continue; } // Delete host. auto host_id = (*key)->getHostId(); key = idx.erase(key); erased_hosts++; // Delete reservations. erased_reservations += idx6.erase(host_id); } LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL6) .arg(erased_hosts) .arg(erased_reservations) .arg(subnet_id) .arg(Host::getIdentifierAsText(identifier_type, identifier_begin, identifier_len)); return (erased_hosts != 0); } bool CfgHosts::setIPReservationsUnique(const bool unique) { ip_reservations_unique_ = unique; return (true); } ElementPtr CfgHosts::toElement() const { uint16_t family = CfgMgr::instance().getFamily(); if (family == AF_INET) { return (toElement4()); } else if (family == AF_INET6) { return (toElement6()); } else { isc_throw(ToElementError, "CfgHosts::toElement: unknown " "address family: " << family); } } ElementPtr CfgHosts::toElement4() const { CfgHostsList result; // Iterate using arbitrary the index 0 const HostContainerIndex0& idx = hosts_.get<0>(); for (HostContainerIndex0::const_iterator host = idx.begin(); host != idx.end(); ++host) { // Convert host to element representation ElementPtr map = (*host)->toElement4(); // Push it on the list SubnetID subnet_id = (*host)->getIPv4SubnetID(); result.add(subnet_id, map); } return (result.externalize()); } ElementPtr CfgHosts::toElement6() const { CfgHostsList result; // Iterate using arbitrary the index 0 const HostContainerIndex0& idx = hosts_.get<0>(); for (HostContainerIndex0::const_iterator host = idx.begin(); host != idx.end(); ++host) { // Convert host to Element representation ElementPtr map = (*host)->toElement6(); // Push it on the list SubnetID subnet_id = (*host)->getIPv6SubnetID(); result.add(subnet_id, map); } return (result.externalize()); } } // end of namespace isc::dhcp } // end of namespace isc