// 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/. #include #include #include #include #include #include #include #include #include #include using namespace isc::asiolink; using namespace isc::data; namespace isc { namespace dhcp { ConstHostCollection CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const { // Do not issue logging message here because it will be logged by // the getAllInternal method. ConstHostCollection collection; getAllInternal(hwaddr, duid, collection); return (collection); } HostCollection CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) { // Do not issue logging message here because it will be logged by // the getAllInternal method. HostCollection collection; getAllInternal(hwaddr, duid, collection); return (collection); } 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 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::getAllInternal(const HWAddrPtr& hwaddr, const DuidPtr& duid, Storage& storage) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_HWADDR_DUID) .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)") .arg(duid ? duid->toText() : "(no-duid)"); // Get hosts using HW address. if (hwaddr && !hwaddr->hwaddr_.empty()) { getAllInternal(Host::IDENT_HWADDR, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size(), storage); } // Get hosts using DUID. if (duid && !duid->getDuid().empty()) { getAllInternal(Host::IDENT_DUID, &duid->getDuid()[0], duid->getDuid().size(), storage); } } 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 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_ADDRESS6_HOST) .arg(address.toText()) .arg((*host)->toText()); storage.push_back(*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 HWAddrPtr& hwaddr, const DuidPtr& duid) const { // Do not log here because getHostInternal logs. // The false value indicates that it is an IPv4 subnet. HostPtr host; if (hwaddr && !hwaddr->hwaddr_.empty()) { host = getHostInternal(subnet_id, false, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size()); } if (!host && duid && !duid->getDuid().empty()) { host = getHostInternal(subnet_id, false, Host::IDENT_DUID, &duid->getDuid()[0], duid->getDuid().size()); } return (host); } HostPtr CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr, const DuidPtr& duid) { // Do not log here because getHostInternal logs. // The false value indicates that it is an IPv4 subnet. HostPtr host; if (hwaddr && !hwaddr->hwaddr_.empty()) { host = getHostInternal(subnet_id, false, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size()); } if (!host && duid && !duid->getDuid().empty()) { host = getHostInternal(subnet_id, false, Host::IDENT_DUID, &duid->getDuid()[0], duid->getDuid().size()); } return (host); } 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()); } ConstHostPtr CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid, const HWAddrPtr& hwaddr) const { // Do not log here because getHostInternal logs. // The true value indicates that it is an IPv6 subnet. HostPtr host; if (duid && !duid->getDuid().empty()) { host = getHostInternal(subnet_id, true, Host::IDENT_DUID, &duid->getDuid()[0], duid->getDuid().size()); } if (!host && hwaddr && !hwaddr->hwaddr_.empty()) { host = getHostInternal(subnet_id, true, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size()); } return (host); } HostPtr CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid, const HWAddrPtr& hwaddr) { // Do not log here because getHostInternal logs. // The true value indicates that it is an IPv6 subnet. HostPtr host; if (duid && !duid->getDuid().empty()) { host = getHostInternal(subnet_id, true, Host::IDENT_DUID, &duid->getDuid()[0], duid->getDuid().size()); } if (!host && hwaddr && !hwaddr->hwaddr_.empty()) { host = getHostInternal(subnet_id, true, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size()); } return (host); } 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)); } 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 non-zero if (host->getIPv4SubnetID() == 0 && host->getIPv6SubnetID() == 0) { 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(); // There should be at least one resource reserved: hostname, IPv4 // address, siaddr, sname, file or IPv6 address or prefix. /// @todo: this check should be done in add(), not in add4() if (host->getHostname().empty() && (host->getIPv4Reservation().isV4Zero()) && !host->hasIPv6Reservation() && host->getNextServer().isV4Zero() && host->getServerHostname().empty() && host->getBootFileName().empty() && host->getCfgOption4()->empty() && host->getCfgOption6()->empty() && host->getClientClasses4().empty() && host->getClientClasses6().empty()) { std::ostringstream s; if (hwaddr) { s << "for DUID: " << hwaddr->toText(); } else if (duid) { s << "for HW address: " << duid->toText(); } isc_throw(BadValue, "specified reservation " << s.str() << " must include at least one resource, i.e. " "hostname, IPv4 address, IPv6 address/prefix, " "options"); } // Check for duplicates for the specified IPv4 subnet. if ((host->getIPv4SubnetID() > 0) && get4(host->getIPv4SubnetID(), hwaddr, duid)) { isc_throw(DuplicateHost, "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() << "' as this host has already been added"); // Check for duplicates for the specified IPv6 subnet. } else if (host->getIPv6SubnetID() && get6(host->getIPv6SubnetID(), duid, hwaddr)) { isc_throw(DuplicateHost, "failed to add new host using the HW" " address '" << (hwaddr ? hwaddr->toText(false) : "(null)") << " and DUID '" << (duid ? duid->toText() : "(null)") << "' 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 (!host->getIPv4Reservation().isV4Zero() && (host->getIPv4SubnetID() > 0) && 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() > 0) && !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. hosts_.insert(host); } void CfgHosts::add6(const HostPtr& host) { if (host->getIPv6SubnetID() == 0) { // 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 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)); } } 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) { // Get the subnet ID SubnetID subnet_id = (*host)->getIPv4SubnetID(); // Prepare the map ElementPtr map = Element::createMap(); // Set the identifier Host::IdentifierType id_type = (*host)->getIdentifierType(); if (id_type == Host::IDENT_HWADDR) { HWAddrPtr hwaddr = (*host)->getHWAddress(); map->set("hw-address", Element::create(hwaddr->toText(false))); } else if (id_type == Host::IDENT_DUID) { DuidPtr duid = (*host)->getDuid(); map->set("duid", Element::create(duid->toText())); } else if (id_type == Host::IDENT_CIRCUIT_ID) { const std::vector& bin = (*host)->getIdentifier(); std::string circuit_id = util::encode::encodeHex(bin); map->set("circuit-id", Element::create(circuit_id)); } else if (id_type == Host::IDENT_CLIENT_ID) { const std::vector& bin = (*host)->getIdentifier(); std::string client_id = util::encode::encodeHex(bin); map->set("client-id", Element::create(client_id)); } else if (id_type == Host::IDENT_FLEX) { const std::vector& bin = (*host)->getIdentifier(); std::string flex = util::encode::encodeHex(bin); map->set("flex-id", Element::create(flex)); } else { isc_throw(ToElementError, "invalid identifier type: " << id_type); } // Set the reservation const IOAddress& address = (*host)->getIPv4Reservation(); map->set("ip-address", Element::create(address.toText())); // Set the hostname const std::string& hostname = (*host)->getHostname(); map->set("hostname", Element::create(hostname)); // Set next-server const IOAddress& next_server = (*host)->getNextServer(); map->set("next-server", Element::create(next_server.toText())); // Set server-hostname const std::string& server_hostname = (*host)->getServerHostname(); map->set("server-hostname", Element::create(server_hostname)); // Set boot-file-name const std::string& boot_file_name = (*host)->getBootFileName(); map->set("boot-file-name", Element::create(boot_file_name)); // Set client-classes const ClientClasses& cclasses = (*host)->getClientClasses4(); ElementPtr classes = Element::createList(); for (ClientClasses::const_iterator cclass = cclasses.cbegin(); cclass != cclasses.end(); ++cclass) { classes->add(Element::create(*cclass)); } map->set("client-classes", classes); // Set option-data ConstCfgOptionPtr opts = (*host)->getCfgOption4(); map->set("option-data", opts->toElement()); // Push the map on the list 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) { // Get the subnet ID SubnetID subnet_id = (*host)->getIPv6SubnetID(); // Prepare the map ElementPtr map = Element::createMap(); // Set the identifier Host::IdentifierType id_type = (*host)->getIdentifierType(); if (id_type == Host::IDENT_HWADDR) { HWAddrPtr hwaddr = (*host)->getHWAddress(); map->set("hw-address", Element::create(hwaddr->toText(false))); } else if (id_type == Host::IDENT_DUID) { DuidPtr duid = (*host)->getDuid(); map->set("duid", Element::create(duid->toText())); } else if (id_type == Host::IDENT_CIRCUIT_ID) { isc_throw(ToElementError, "unexpected circuit-id DUID type"); } else if (id_type == Host::IDENT_CLIENT_ID) { isc_throw(ToElementError, "unexpected client-id DUID type"); } else if (id_type == Host::IDENT_FLEX) { const std::vector& bin = (*host)->getIdentifier(); std::string flex = util::encode::encodeHex(bin); map->set("flex-id", Element::create(flex)); } else { isc_throw(ToElementError, "invalid DUID type: " << id_type); } // Set reservations (ip-addresses) IPv6ResrvRange na_resv = (*host)->getIPv6Reservations(IPv6Resrv::TYPE_NA); ElementPtr resvs = Element::createList(); for (IPv6ResrvIterator resv = na_resv.first; resv != na_resv.second; ++resv) { resvs->add(Element::create(resv->second.toText())); } map->set("ip-addresses", resvs); // Set reservations (prefixes) IPv6ResrvRange pd_resv = (*host)->getIPv6Reservations(IPv6Resrv::TYPE_PD); resvs = Element::createList(); for (IPv6ResrvIterator resv = pd_resv.first; resv != pd_resv.second; ++resv) { resvs->add(Element::create(resv->second.toText())); } map->set("prefixes", resvs); // Set the hostname const std::string& hostname = (*host)->getHostname(); map->set("hostname", Element::create(hostname)); // Set client-classes const ClientClasses& cclasses = (*host)->getClientClasses6(); ElementPtr classes = Element::createList(); for (ClientClasses::const_iterator cclass = cclasses.cbegin(); cclass != cclasses.end(); ++cclass) { classes->add(Element::create(*cclass)); } map->set("client-classes", classes); // Set option-data ConstCfgOptionPtr opts = (*host)->getCfgOption6(); map->set("option-data", opts->toElement()); // Push the map on the list result.add(subnet_id, map); } return (result.externalize()); } } // end of namespace isc::dhcp } // end of namespace isc