mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 04:57:52 +00:00
[5307] Assign reserved hostnames for shared networks.
This commit is contained in:
parent
70678b349a
commit
dd3e3a9101
@ -2461,6 +2461,8 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
|
|||||||
appendRequestedOptions(solicit, response, co_list);
|
appendRequestedOptions(solicit, response, co_list);
|
||||||
appendRequestedVendorOptions(solicit, response, ctx, co_list);
|
appendRequestedVendorOptions(solicit, response, ctx, co_list);
|
||||||
|
|
||||||
|
updateReservedFqdn(ctx, response);
|
||||||
|
|
||||||
// Only generate name change requests if sending a Reply as a result
|
// Only generate name change requests if sending a Reply as a result
|
||||||
// of receiving Rapid Commit option.
|
// of receiving Rapid Commit option.
|
||||||
if (response->getType() == DHCPV6_REPLY) {
|
if (response->getType() == DHCPV6_REPLY) {
|
||||||
@ -2492,6 +2494,7 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
|
|||||||
appendRequestedOptions(request, reply, co_list);
|
appendRequestedOptions(request, reply, co_list);
|
||||||
appendRequestedVendorOptions(request, reply, ctx, co_list);
|
appendRequestedVendorOptions(request, reply, ctx, co_list);
|
||||||
|
|
||||||
|
updateReservedFqdn(ctx, reply);
|
||||||
generateFqdn(reply);
|
generateFqdn(reply);
|
||||||
createNameChangeRequests(reply, ctx);
|
createNameChangeRequests(reply, ctx);
|
||||||
|
|
||||||
@ -2520,6 +2523,7 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
|
|||||||
appendRequestedOptions(renew, reply, co_list);
|
appendRequestedOptions(renew, reply, co_list);
|
||||||
appendRequestedVendorOptions(renew, reply, ctx, co_list);
|
appendRequestedVendorOptions(renew, reply, ctx, co_list);
|
||||||
|
|
||||||
|
updateReservedFqdn(ctx, reply);
|
||||||
generateFqdn(reply);
|
generateFqdn(reply);
|
||||||
createNameChangeRequests(reply, ctx);
|
createNameChangeRequests(reply, ctx);
|
||||||
|
|
||||||
@ -2548,6 +2552,7 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
|
|||||||
appendRequestedOptions(rebind, reply, co_list);
|
appendRequestedOptions(rebind, reply, co_list);
|
||||||
appendRequestedVendorOptions(rebind, reply, ctx, co_list);
|
appendRequestedVendorOptions(rebind, reply, ctx, co_list);
|
||||||
|
|
||||||
|
updateReservedFqdn(ctx, reply);
|
||||||
generateFqdn(reply);
|
generateFqdn(reply);
|
||||||
createNameChangeRequests(reply, ctx);
|
createNameChangeRequests(reply, ctx);
|
||||||
|
|
||||||
@ -3098,6 +3103,44 @@ Dhcpv6Srv::setReservedClientClasses(const Pkt6Ptr& pkt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Dhcpv6Srv::updateReservedFqdn(const AllocEngine::ClientContext6& ctx,
|
||||||
|
const Pkt6Ptr& answer) {
|
||||||
|
if (!answer) {
|
||||||
|
isc_throw(isc::Unexpected, "an instance of the object encapsulating"
|
||||||
|
" a message must not be NULL when updating reserved FQDN");
|
||||||
|
}
|
||||||
|
|
||||||
|
Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>
|
||||||
|
(answer->getOption(D6O_CLIENT_FQDN));
|
||||||
|
|
||||||
|
// If Client FQDN option is not included, there is nothing to do.
|
||||||
|
if (!fqdn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name = fqdn->getDomainName();
|
||||||
|
|
||||||
|
// If there is a host reservation for this client we have to check whether
|
||||||
|
// this reservation has the same hostname as the hostname currently
|
||||||
|
// present in the FQDN option. If not, it indicates that the allocation
|
||||||
|
// engine picked a different subnet (from within a shared network) for
|
||||||
|
// reservations and we have to send this new value to the client.
|
||||||
|
if (ctx.currentHost() &&
|
||||||
|
!ctx.currentHost()->getHostname().empty()) {
|
||||||
|
std::string new_name = CfgMgr::instance().getD2ClientMgr().
|
||||||
|
qualifyName(ctx.currentHost()->getHostname(), true);
|
||||||
|
|
||||||
|
if (new_name != name) {
|
||||||
|
fqdn->setDomainName(new_name, Option6ClientFqdn::FULL);
|
||||||
|
|
||||||
|
// Replace previous instance of Client FQDN option.
|
||||||
|
answer->delOption(D6O_CLIENT_FQDN);
|
||||||
|
answer->addOption(fqdn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer) {
|
Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer) {
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
@ -3162,6 +3205,9 @@ Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer) {
|
|||||||
// Set the generated FQDN in the Client FQDN option.
|
// Set the generated FQDN in the Client FQDN option.
|
||||||
fqdn->setDomainName(generated_name, Option6ClientFqdn::FULL);
|
fqdn->setDomainName(generated_name, Option6ClientFqdn::FULL);
|
||||||
|
|
||||||
|
answer->delOption(D6O_CLIENT_FQDN);
|
||||||
|
answer->addOption(fqdn);
|
||||||
|
|
||||||
} catch (const Exception& ex) {
|
} catch (const Exception& ex) {
|
||||||
LOG_ERROR(ddns6_logger, DHCP6_DDNS_GENERATED_FQDN_UPDATE_FAIL)
|
LOG_ERROR(ddns6_logger, DHCP6_DDNS_GENERATED_FQDN_UPDATE_FAIL)
|
||||||
.arg(answer->getLabel())
|
.arg(answer->getLabel())
|
||||||
|
@ -764,6 +764,25 @@ private:
|
|||||||
/// @param classes a reference to added class names for logging
|
/// @param classes a reference to added class names for logging
|
||||||
void classifyByVendor(const Pkt6Ptr& pkt, std::string& classes);
|
void classifyByVendor(const Pkt6Ptr& pkt, std::string& classes);
|
||||||
|
|
||||||
|
/// @brief Update FQDN based on the reservations in the current subnet.
|
||||||
|
///
|
||||||
|
/// When shared networks are in use the allocation engine may switch to
|
||||||
|
/// a different subnet than originally selected. If this new subnet has
|
||||||
|
/// hostname reservations there is a need to update the FQDN option
|
||||||
|
/// value.
|
||||||
|
///
|
||||||
|
/// This method should be called after lease assignments to perform
|
||||||
|
/// such update when required.
|
||||||
|
///
|
||||||
|
/// @param ctx Client context.
|
||||||
|
/// @param answer Message being sent to a client, which may hold an FQDN
|
||||||
|
/// option to be updated.
|
||||||
|
///
|
||||||
|
/// @throw isc::Unexpected if specified message is NULL. This is treated
|
||||||
|
/// as a programmatic error.
|
||||||
|
void updateReservedFqdn(const AllocEngine::ClientContext6& ctx,
|
||||||
|
const Pkt6Ptr& answer);
|
||||||
|
|
||||||
/// @private
|
/// @private
|
||||||
/// @brief Generate FQDN to be sent to a client if none exists.
|
/// @brief Generate FQDN to be sent to a client if none exists.
|
||||||
///
|
///
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <dhcp/option_int.h>
|
#include <dhcp/option_int.h>
|
||||||
#include <dhcp/option6_client_fqdn.h>
|
#include <dhcp/option6_client_fqdn.h>
|
||||||
#include <dhcp/tests/iface_mgr_test_config.h>
|
#include <dhcp/tests/iface_mgr_test_config.h>
|
||||||
|
#include <dhcpsrv/lease_mgr_factory.h>
|
||||||
#include <dhcp6/tests/dhcp6_client.h>
|
#include <dhcp6/tests/dhcp6_client.h>
|
||||||
#include <dhcp6/tests/dhcp6_test_utils.h>
|
#include <dhcp6/tests/dhcp6_test_utils.h>
|
||||||
#include <stats/stats_mgr.h>
|
#include <stats/stats_mgr.h>
|
||||||
@ -599,12 +600,13 @@ const char* NETWORKS_CONFIG[] = {
|
|||||||
" \"id\": 100,"
|
" \"id\": 100,"
|
||||||
" \"pools\": ["
|
" \"pools\": ["
|
||||||
" {"
|
" {"
|
||||||
" \"pool\": \"2001:db8:2::20 - 2001:db8:2::20\""
|
" \"pool\": \"2001:db8:2::20 - 2001:db8:2::30\""
|
||||||
" }"
|
" }"
|
||||||
" ],"
|
" ],"
|
||||||
" \"reservations\": ["
|
" \"reservations\": ["
|
||||||
" {"
|
" {"
|
||||||
" \"duid\": \"00:03:00:01:11:22:33:44:55:66\","
|
" \"duid\": \"00:03:00:01:11:22:33:44:55:66\","
|
||||||
|
" \"ip-addresses\": [ \"2001:db8:2::20\" ],"
|
||||||
" \"hostname\": \"test.example.org\","
|
" \"hostname\": \"test.example.org\","
|
||||||
" \"client-classes\": [ \"class-with-dns-servers\" ]"
|
" \"client-classes\": [ \"class-with-dns-servers\" ]"
|
||||||
" }"
|
" }"
|
||||||
@ -1119,6 +1121,12 @@ TEST_F(Dhcpv6SharedNetworkTest, variousFieldsInReservation) {
|
|||||||
ASSERT_TRUE(fqdn);
|
ASSERT_TRUE(fqdn);
|
||||||
ASSERT_EQ("test.example.org.", fqdn->getDomainName());
|
ASSERT_EQ("test.example.org.", fqdn->getDomainName());
|
||||||
|
|
||||||
|
// Make sure that the correct hostname has been stored in the database.
|
||||||
|
Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
|
||||||
|
IOAddress("2001:db8:2::20"));
|
||||||
|
ASSERT_TRUE(lease);
|
||||||
|
EXPECT_EQ("test.example.org.", lease->hostname_);
|
||||||
|
|
||||||
// The DNS servers option should be derived from the client class based on the
|
// The DNS servers option should be derived from the client class based on the
|
||||||
// static class reservations.
|
// static class reservations.
|
||||||
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NAME_SERVERS, "2001:db8:1::50"));
|
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NAME_SERVERS, "2001:db8:1::50"));
|
||||||
|
@ -388,7 +388,7 @@ namespace isc {
|
|||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
|
|
||||||
AllocEngine::ClientContext6::ClientContext6()
|
AllocEngine::ClientContext6::ClientContext6()
|
||||||
: query_(), fake_allocation_(false), subnet_(), duid_(),
|
: query_(), fake_allocation_(false), subnet_(), host_subnet_(), duid_(),
|
||||||
hwaddr_(), host_identifiers_(), hosts_(), fwd_dns_update_(false),
|
hwaddr_(), host_identifiers_(), hosts_(), fwd_dns_update_(false),
|
||||||
rev_dns_update_(false), hostname_(), callout_handle_(),
|
rev_dns_update_(false), hostname_(), callout_handle_(),
|
||||||
ias_() {
|
ias_() {
|
||||||
@ -443,8 +443,9 @@ isAllocated(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const {
|
|||||||
|
|
||||||
ConstHostPtr
|
ConstHostPtr
|
||||||
AllocEngine::ClientContext6::currentHost() const {
|
AllocEngine::ClientContext6::currentHost() const {
|
||||||
if (subnet_) {
|
Subnet6Ptr subnet = host_subnet_ ? host_subnet_ : subnet_;
|
||||||
auto host = hosts_.find(subnet_->getID());
|
if (subnet) {
|
||||||
|
auto host = hosts_.find(subnet->getID());
|
||||||
if (host != hosts_.cend()) {
|
if (host != hosts_.cend()) {
|
||||||
return (host->second);
|
return (host->second);
|
||||||
}
|
}
|
||||||
@ -853,6 +854,7 @@ void
|
|||||||
AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
||||||
Lease6Collection& existing_leases) {
|
Lease6Collection& existing_leases) {
|
||||||
|
|
||||||
|
|
||||||
// If there are no reservations or the reservation is v4, there's nothing to do.
|
// If there are no reservations or the reservation is v4, there's nothing to do.
|
||||||
if (ctx.hosts_.empty()) {
|
if (ctx.hosts_.empty()) {
|
||||||
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
|
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
|
||||||
@ -881,6 +883,37 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
|||||||
.arg(lease->typeToText(lease->type_))
|
.arg(lease->typeToText(lease->type_))
|
||||||
.arg(lease->addr_.toText());
|
.arg(lease->addr_.toText());
|
||||||
|
|
||||||
|
// Besides IP reservations we're also going to return other reserved
|
||||||
|
// parameters, such as hostname. We want to hand out the hostname value
|
||||||
|
// from the same reservation entry as IP addresses. Thus, let's see if
|
||||||
|
// there is any hostname reservation.
|
||||||
|
if (!ctx.host_subnet_) {
|
||||||
|
SharedNetwork6Ptr network;
|
||||||
|
ctx.subnet_->getSharedNetwork(network);
|
||||||
|
if (network) {
|
||||||
|
// Remember the subnet that holds this preferred host
|
||||||
|
// reservation. The server will use it to return appropriate
|
||||||
|
// FQDN, classes etc.
|
||||||
|
ctx.host_subnet_ = network->getSubnet(lease->subnet_id_);
|
||||||
|
ConstHostPtr host = ctx.hosts_[lease->subnet_id_];
|
||||||
|
// If there is a hostname reservation here we should stick
|
||||||
|
// to this reservation. By updating the hostname in the
|
||||||
|
// context we make sure that the database is updated with
|
||||||
|
// this new value and the server doesn't need to do it and
|
||||||
|
// its processing performance is not impacted by the hostname
|
||||||
|
// updates.
|
||||||
|
if (host && !host->getHostname().empty()) {
|
||||||
|
// We have to determine whether the hostname is generated
|
||||||
|
// in response to client's FQDN or not. If yes, we will
|
||||||
|
// need to qualify the hostname. Otherwise, we just use
|
||||||
|
// the hostname as it is specified for the reservation.
|
||||||
|
OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
|
||||||
|
ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
|
||||||
|
qualifyName(host->getHostname(), static_cast<bool>(fqdn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If this is a real allocation, we may need to extend the lease
|
// If this is a real allocation, we may need to extend the lease
|
||||||
// lifetime.
|
// lifetime.
|
||||||
if (!ctx.fake_allocation_ && conditionalExtendLifetime(*lease)) {
|
if (!ctx.fake_allocation_ && conditionalExtendLifetime(*lease)) {
|
||||||
@ -919,7 +952,6 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
|||||||
// We have allocated this address/prefix while processing one of the
|
// We have allocated this address/prefix while processing one of the
|
||||||
// previous IAs, so let's try another reservation.
|
// previous IAs, so let's try another reservation.
|
||||||
if (ctx.isAllocated(addr, prefix_len)) {
|
if (ctx.isAllocated(addr, prefix_len)) {
|
||||||
std::cout << "is allocated " << addr << std::endl;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -931,11 +963,35 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
|||||||
// Ok, let's create a new lease...
|
// Ok, let's create a new lease...
|
||||||
ctx.subnet_ = subnet;
|
ctx.subnet_ = subnet;
|
||||||
|
|
||||||
|
// Let's remember the subnet from which the reserved address has been
|
||||||
|
// allocated. We'll use this subnet for allocating other reserved
|
||||||
|
// resources.
|
||||||
|
if (!ctx.host_subnet_) {
|
||||||
|
ctx.host_subnet_ = subnet;
|
||||||
|
if (!host->getHostname().empty()) {
|
||||||
|
// If there is a hostname reservation here we should stick
|
||||||
|
// to this reservation. By updating the hostname in the
|
||||||
|
// context we make sure that the database is updated with
|
||||||
|
// this new value and the server doesn't need to do it and
|
||||||
|
// its processing performance is not impacted by the hostname
|
||||||
|
// updates.
|
||||||
|
|
||||||
|
// We have to determine whether the hostname is generated
|
||||||
|
// in response to client's FQDN or not. If yes, we will
|
||||||
|
// need to qualify the hostname. Otherwise, we just use
|
||||||
|
// the hostname as it is specified for the reservation.
|
||||||
|
OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
|
||||||
|
ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
|
||||||
|
qualifyName(host->getHostname(), static_cast<bool>(fqdn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Lease6Ptr lease = createLease6(ctx, addr, prefix_len);
|
Lease6Ptr lease = createLease6(ctx, addr, prefix_len);
|
||||||
|
|
||||||
// ... and add it to the existing leases list.
|
// ... and add it to the existing leases list.
|
||||||
existing_leases.push_back(lease);
|
existing_leases.push_back(lease);
|
||||||
|
|
||||||
|
|
||||||
if (ctx.currentIA().type_ == Lease::TYPE_NA) {
|
if (ctx.currentIA().type_ == Lease::TYPE_NA) {
|
||||||
LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_HR_ADDR_GRANTED)
|
LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_HR_ADDR_GRANTED)
|
||||||
.arg(addr.toText())
|
.arg(addr.toText())
|
||||||
|
@ -305,6 +305,11 @@ public:
|
|||||||
/// @brief Subnet selected for the client by the server.
|
/// @brief Subnet selected for the client by the server.
|
||||||
Subnet6Ptr subnet_;
|
Subnet6Ptr subnet_;
|
||||||
|
|
||||||
|
/// @brief Subnet from which host reservations should be retrieved.
|
||||||
|
///
|
||||||
|
/// It can be NULL, in which case @c subnet_ value is used.
|
||||||
|
Subnet6Ptr host_subnet_;
|
||||||
|
|
||||||
/// @brief Client identifier
|
/// @brief Client identifier
|
||||||
DuidPtr duid_;
|
DuidPtr duid_;
|
||||||
|
|
||||||
@ -443,7 +448,7 @@ public:
|
|||||||
ias_.push_back(IAContext());
|
ias_.push_back(IAContext());
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Returns host for currently selected subnet.
|
/// @brief Returns host from the most preferred subnet.
|
||||||
///
|
///
|
||||||
/// @return Pointer to the host object.
|
/// @return Pointer to the host object.
|
||||||
ConstHostPtr currentHost() const;
|
ConstHostPtr currentHost() const;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user