2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[#3049] Added params to Pool, modified DdnsParams

src/bin/dhcp4/dhcp4_srv.cc
    Dhcpv4Srv::assignLeased - modified to set pool on DdnsParams
    and reprocess client name if need be.

src/lib/dhcpsrv/ncr_generator.cc
    queueNCRCommon() - changed to accept a ConstSubnetPtr and to
    create DdnsParams instance for the subnet and lease for fetching
    parameter values.

src/lib/dhcpsrv/network.h
    Corrected a pre-existing typo

src/lib/dhcpsrv/pool.*
    Added DDNS behavioral parameters and accessors
    Pool::hasDdnsParameters() - new function

src/lib/dhcpsrv/srv_config.*
    Added pool instance to DdnsParams
    Modified DdnsParams accessors to try pool first

src/lib/dhcpsrv/tests/pool_unittest.cc
    TEST_F(PoolTest, ddnsParameters4)
    TEST_F(PoolTest, ddnsParameters6) - new tests

src/lib/dhcpsrv/tests/srv_config_unittest.cc
    TEST_F(DdnsParamsTest, checkDdnsParameters4)
    TEST_F(DdnsParamsTest, checkDdnsParameters6) - new tests
This commit is contained in:
Thomas Markwalder
2024-12-20 12:19:42 -05:00
parent 32e6e677ea
commit 1f7dddcfab
9 changed files with 904 additions and 71 deletions

View File

@@ -3177,13 +3177,25 @@ Dhcpv4Srv::assignLease(Dhcpv4Exchange& ex) {
// Get a lease.
Lease4Ptr lease = alloc_engine_->allocateLease4(*ctx);
// Tracks whether or not the client name (FQDN or host) has changed since
// the lease was allocated.
bool client_name_changed = false;
bool reprocess_client_name = false;
if (lease) {
auto ddns_params = ex.getContext()->getDdnsParams();
auto pool = ddns_params->setPoolFromAddress(lease->addr_);
if (pool) {
reprocess_client_name = pool->hasDdnsParameters();
}
}
// Subnet may be modified by the allocation engine, if the initial subnet
// belongs to a shared network.
if (subnet && ctx->subnet_ && subnet->getID() != ctx->subnet_->getID()) {
// We changed subnets and that means DDNS parameters might be different
// so we need to rerun client name processing logic. Arguably we could
// compare DDNS parameters for both subnets and then decide if we need
// to rerun the name logic, but that's not likely to be any faster than
// just re-running the name logic. @todo When inherited parameter
// performance is improved this argument could be revisited.
// Another case is the new subnet has a reserved hostname.
SharedNetwork4Ptr network;
subnet->getSharedNetwork(network);
LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_SUBNET_DYNAMICALLY_CHANGED)
@@ -3193,37 +3205,36 @@ Dhcpv4Srv::assignLease(Dhcpv4Exchange& ex) {
.arg(network ? network->getName() : "<no network?>");
subnet = ctx->subnet_;
if (lease) {
// We changed subnets and that means DDNS parameters might be different
// so we need to rerun client name processing logic. Arguably we could
// compare DDNS parameters for both subnets and then decide if we need
// to rerun the name logic, but that's not likely to be any faster than
// just re-running the name logic. @todo When inherited parameter
// performance is improved this argument could be revisited.
// Another case is the new subnet has a reserved hostname.
reprocess_client_name = true;
}
}
// First, we need to remove the prior values from the response and reset
// those in context, to give processClientName a clean slate.
resp->delOption(DHO_FQDN);
resp->delOption(DHO_HOST_NAME);
ctx->hostname_ = "";
ctx->fwd_dns_update_ = false;
ctx->rev_dns_update_ = false;
// Tracks whether or not the client name (FQDN or host) has changed since
// the lease was allocated.
bool client_name_changed = false;
// Regenerate the name and dns flags.
processClientName(ex);
if (reprocess_client_name) {
// First, we need to remove the prior values from the response and reset
// those in context, to give processClientName a clean slate.
resp->delOption(DHO_FQDN);
resp->delOption(DHO_HOST_NAME);
ctx->hostname_ = "";
ctx->fwd_dns_update_ = false;
ctx->rev_dns_update_ = false;
// If the results are different from the values already on the
// lease, flag it so the lease gets updated down below.
if ((lease->hostname_ != ctx->hostname_) ||
(lease->fqdn_fwd_ != ctx->fwd_dns_update_) ||
(lease->fqdn_rev_ != ctx->rev_dns_update_)) {
lease->hostname_ = ctx->hostname_;
lease->fqdn_fwd_ = ctx->fwd_dns_update_;
lease->fqdn_rev_ = ctx->rev_dns_update_;
client_name_changed = true;
}
// Regenerate the name and dns flags.
processClientName(ex);
// If the results are different from the values already on the
// lease, flag it so the lease gets updated down below.
if ((lease->hostname_ != ctx->hostname_) ||
(lease->fqdn_fwd_ != ctx->fwd_dns_update_) ||
(lease->fqdn_rev_ != ctx->rev_dns_update_)) {
lease->hostname_ = ctx->hostname_;
lease->fqdn_fwd_ = ctx->fwd_dns_update_;
lease->fqdn_rev_ = ctx->rev_dns_update_;
client_name_changed = true;
}
}

View File

@@ -39,7 +39,7 @@ namespace {
template<typename LeasePtrType, typename IdentifierType>
void queueNCRCommon(const NameChangeType& chg_type, const LeasePtrType& lease,
const IdentifierType& identifier, const std::string& label,
NetworkPtr subnet) {
const ConstSubnetPtr subnet) {
// Check if there is a need for update.
if (lease->hostname_.empty() || (!lease->fqdn_fwd_ && !lease->fqdn_rev_)
|| !CfgMgr::instance().getD2ClientMgr().ddnsEnabled()) {
@@ -51,22 +51,26 @@ void queueNCRCommon(const NameChangeType& chg_type, const LeasePtrType& lease,
return;
}
ConflictResolutionMode conflict_resolution_mode = CHECK_WITH_DHCID;
util::Optional<double> ddns_ttl_percent;
util::Optional<uint32_t> ddns_ttl;
util::Optional<uint32_t> ddns_ttl_min;
util::Optional<uint32_t> ddns_ttl_max;
if (subnet) {
auto mode = subnet->getDdnsConflictResolutionMode();
if (!mode.empty()) {
conflict_resolution_mode = StringToConflictResolutionMode(mode);
}
ConflictResolutionMode conflict_resolution_mode = CHECK_WITH_DHCID;
util::Optional<double> ddns_ttl_percent;
util::Optional<uint32_t> ddns_ttl;
util::Optional<uint32_t> ddns_ttl_min;
util::Optional<uint32_t> ddns_ttl_max;
if (subnet) {
// Create a DdnsParams so we have access to pool scope values.
DdnsParams ddns_params(subnet, true);
static_cast<void>(ddns_params.setPoolFromAddress(lease->addr_));
ddns_ttl_percent = subnet->getDdnsTtlPercent();
ddns_ttl = subnet->getDdnsTtl();
ddns_ttl_min = subnet->getDdnsTtlMin();
ddns_ttl_max = subnet->getDdnsTtlMax();
}
auto mode = ddns_params.getConflictResolutionMode();
if (!mode.empty()) {
conflict_resolution_mode = StringToConflictResolutionMode(mode);
}
ddns_ttl_percent = ddns_params.getTtlPercent();
ddns_ttl = ddns_params.getTtl();
ddns_ttl_min = ddns_params.getTtlMin();
ddns_ttl_max = ddns_params.getTtlMax();
}
try {
// Create DHCID
@@ -112,8 +116,8 @@ void queueNCR(const NameChangeType& chg_type, const Lease4Ptr& lease) {
if (lease) {
// Figure out from the lease's subnet if we should use conflict resolution.
// If there's no subnet, something hinky is going on so we'll set it true.
Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()
->getCfgSubnets4()->getSubnet(lease->subnet_id_);
ConstSubnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()
->getCfgSubnets4()->getSubnet(lease->subnet_id_);
// Client id takes precedence over HW address.
if (lease->client_id_) {
@@ -133,7 +137,7 @@ void queueNCR(const NameChangeType& chg_type, const Lease6Ptr& lease) {
if (lease && (lease->type_ != Lease::TYPE_PD) && lease->duid_) {
// Figure out from the lease's subnet if we should use conflict resolution.
// If there's no subnet, something hinky is going on so we'll set it true.
Subnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()
ConstSubnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()
->getCfgSubnets6()->getSubnet(lease->subnet_id_);
queueNCRCommon(chg_type, lease, *(lease->duid_),
Pkt6::makeLabel(lease->duid_, lease->hwaddr_), subnet);

View File

@@ -834,8 +834,7 @@ public:
ddns_update_on_renew_ = ddns_update_on_renew;
}
/// @brief Returns ib-ddns-conflict-resolution-mode
/// @brief Returns ddns-conflict-resolution-mode
///
/// @param inheritance inheritance mode to be used.
util::Optional<std::string>
@@ -846,7 +845,7 @@ public:
CfgGlobals::DDNS_CONFLICT_RESOLUTION_MODE));
}
/// @brief Sets new ib-ddns-conflict-resolution-mode
/// @brief Sets new ddns-conflict-resolution-mode
///
/// @param ddns_conflict_resolution_mode New value to use.
void setDdnsConflictResolutionMode(const util::Optional<std::string>& ddns_conflict_resolution_mode) {

View File

@@ -56,6 +56,24 @@ Pool::toText() const {
return (tmp.str());
}
bool
Pool::hasDdnsParameters() {
return (!(ddns_send_updates_.unspecified() &&
ddns_override_no_update_.unspecified() &&
ddns_override_client_update_.unspecified() &&
ddns_replace_client_name_mode_.unspecified() &&
ddns_generated_prefix_.unspecified() &&
ddns_qualifying_suffix_.unspecified() &&
ddns_update_on_renew_.unspecified() &&
ddns_conflict_resolution_mode_.unspecified() &&
ddns_ttl_percent_.unspecified() &&
ddns_ttl_.unspecified() &&
ddns_ttl_min_.unspecified() &&
ddns_ttl_max_.unspecified() &&
hostname_char_set_.unspecified() &&
hostname_char_replacement_.unspecified()));
}
Pool4::Pool4(const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
: Pool(Lease::TYPE_V4, first, last) {
@@ -142,6 +160,67 @@ Pool::toElement() const {
map->set("pool-id", Element::create(static_cast<long long>(id_)));
}
// Add in DDNS paramters for non-prefix pools.
if (type_ != Lease::TYPE_PD) {
if (!ddns_send_updates_.unspecified()) {
map->set("ddns-send-updates", Element::create(ddns_send_updates_));
}
if (!ddns_override_no_update_.unspecified()) {
map->set("ddns-override-no-update", Element::create(ddns_override_no_update_));
}
if (!ddns_override_client_update_.unspecified()) {
map->set("ddns-override-client-update", Element::create(ddns_override_client_update_));
}
if (!ddns_replace_client_name_mode_.unspecified()) {
map->set("ddns-replace-client-name",
Element::create(D2ClientConfig::
replaceClientNameModeToString(ddns_replace_client_name_mode_)));
}
if (!ddns_generated_prefix_.unspecified()) {
map->set("ddns-generated-prefix", Element::create(ddns_generated_prefix_));
}
if (!ddns_qualifying_suffix_.unspecified()) {
map->set("ddns-qualifying-suffix", Element::create(ddns_qualifying_suffix_));
}
if (!ddns_update_on_renew_.unspecified()) {
map->set("ddns-update-on-renew", Element::create(ddns_update_on_renew_));
}
if (!ddns_conflict_resolution_mode_.unspecified()) {
map->set("ddns-conflict-resolution-mode", Element::create(ddns_conflict_resolution_mode_));
}
if (!ddns_ttl_percent_.unspecified()) {
map->set("ddns-ttl-percent", Element::create(ddns_ttl_percent_));
}
if (!ddns_ttl_.unspecified()) {
map->set("ddns-ttl", Element::create(ddns_ttl_));
}
if (!ddns_ttl_min_.unspecified()) {
map->set("ddns-ttl-min", Element::create(ddns_ttl_min_));
}
if (!ddns_ttl_max_.unspecified()) {
map->set("ddns-ttl-max", Element::create(ddns_ttl_max_));
}
if (!hostname_char_set_.unspecified()) {
map->set("hostname-char-set", Element::create(hostname_char_set_));
}
if (!hostname_char_replacement_.unspecified()) {
map->set("hostname-char-replacement", Element::create(hostname_char_replacement_));
}
}
return (map);
}

View File

@@ -14,9 +14,11 @@
#include <dhcp/option6_pdexclude.h>
#include <dhcpsrv/allocation_state.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/d2_client_cfg.h>
#include <dhcpsrv/lease.h>
#include <dhcpsrv/ip_range_permutation.h>
#include <util/bigints.h>
#include <util/optional.h>
#include <boost/shared_ptr.hpp>
@@ -169,6 +171,199 @@ public:
/// @return A pointer to unparsed pool configuration.
virtual data::ElementPtr toElement() const;
/// @brief Returns ddns-send-updates
util::Optional<bool>
getDdnsSendUpdates() const {
return (ddns_send_updates_);
}
/// @brief Sets new ddns-send-updates
///
/// @param ddns_send_updates New value to use.
void setDdnsSendUpdates(const util::Optional<bool>& ddns_send_updates) {
ddns_send_updates_ = ddns_send_updates;
}
/// @brief Returns ddns-override-no-update
util::Optional<bool>
getDdnsOverrideNoUpdate() const {
return (ddns_override_no_update_);
}
/// @brief Sets new ddns-override-no-update
///
/// @param ddns_override_no_update New value to use.
void setDdnsOverrideNoUpdate(const util::Optional<bool>& ddns_override_no_update) {
ddns_override_no_update_ = ddns_override_no_update;
}
/// @brief Returns ddns-override-client-update
util::Optional<bool>
getDdnsOverrideClientUpdate() const {
return (ddns_override_client_update_);
}
/// @brief Sets new ddns-override-client-update
///
/// @param ddns_override_client_update New value to use.
void setDdnsOverrideClientUpdate(const util::Optional<bool>&
ddns_override_client_update) {
ddns_override_client_update_ = ddns_override_client_update;
}
/// @brief Returns ddns-replace-client-name-mode
util::Optional<D2ClientConfig::ReplaceClientNameMode>
getDdnsReplaceClientNameMode() const {
return (ddns_replace_client_name_mode_);
}
/// @brief Sets new ddns-replace-client-name-mode
///
/// @param ddns_replace_client_name_mode New value to use.
void
setDdnsReplaceClientNameMode(const util::Optional<D2ClientConfig::ReplaceClientNameMode>&
ddns_replace_client_name_mode) {
ddns_replace_client_name_mode_ = ddns_replace_client_name_mode;
}
/// @brief Returns ddns-generated-prefix
util::Optional<std::string>
getDdnsGeneratedPrefix() const {
return (ddns_generated_prefix_);
}
/// @brief Sets new ddns-generated-prefix
///
/// @param ddns_generated_prefix New value to use.
void setDdnsGeneratedPrefix(const util::Optional<std::string>& ddns_generated_prefix) {
ddns_generated_prefix_ = ddns_generated_prefix;
}
/// @brief Returns ddns-qualifying-suffix
util::Optional<std::string>
getDdnsQualifyingSuffix() const {
return (ddns_qualifying_suffix_);
}
/// @brief Sets new ddns-qualifying-suffix
///
/// @param ddns_qualifying_suffix New value to use.
void setDdnsQualifyingSuffix(const util::Optional<std::string>& ddns_qualifying_suffix) {
ddns_qualifying_suffix_ = ddns_qualifying_suffix;
}
/// @brief Returns ddns-update-on-renew
util::Optional<bool>
getDdnsUpdateOnRenew() const {
return (ddns_update_on_renew_);
}
/// @brief Sets new ddns-update-on-renew
///
/// @param ddns_update_on_renew New value to use.
void setDdnsUpdateOnRenew(const util::Optional<bool>& ddns_update_on_renew) {
ddns_update_on_renew_ = ddns_update_on_renew;
}
/// @brief Returns ddns-conflict-resolution-mode
util::Optional<std::string>
getDdnsConflictResolutionMode() const {
return (ddns_conflict_resolution_mode_);
}
/// @brief Sets new ddns-conflict-resolution-mode
///
/// @param ddns_conflict_resolution_mode New value to use.
void setDdnsConflictResolutionMode(const util::Optional<std::string>& ddns_conflict_resolution_mode) {
ddns_conflict_resolution_mode_ = ddns_conflict_resolution_mode;
}
/// @brief Returns ddns-ttl-percent
util::Optional<double>
getDdnsTtlPercent() const {
return (ddns_ttl_percent_);
}
/// @brief Sets new ddns-ttl-percent
///
/// @param ddns_ttl_percent New value to use.
void setDdnsTtlPercent(const util::Optional<double>& ddns_ttl_percent) {
ddns_ttl_percent_ = ddns_ttl_percent;
}
/// @brief Returns ddns-ttl
util::Optional<uint32_t>
getDdnsTtl() const {
return (ddns_ttl_);
}
/// @brief Sets new ddns-ttl
///
/// @param ddns_ttl New value to use.
void setDdnsTtl(const util::Optional<uint32_t>& ddns_ttl) {
ddns_ttl_ = ddns_ttl;
}
/// @brief Returns ddns-ttl-min
util::Optional<uint32_t>
getDdnsTtlMin() const {
return (ddns_ttl_min_);
}
/// @brief Sets new ddns-ttl-min
///
/// @param ddns_ttl_min New value to use.
void setDdnsTtlMin(const util::Optional<uint32_t>& ddns_ttl_min) {
ddns_ttl_min_ = ddns_ttl_min;
}
/// @brief Returns ddns-ttl-max
util::Optional<uint32_t>
getDdnsTtlMax() const {
return (ddns_ttl_max_);
}
/// @brief Sets new ddns-ttl-max
///
/// @param ddns_ttl_max New value to use.
void setDdnsTtlMax(const util::Optional<uint32_t>& ddns_ttl_max) {
ddns_ttl_max_ = ddns_ttl_max;
}
/// @brief Return the char set regexp used to sanitize client hostnames.
util::Optional<std::string>
getHostnameCharSet() const {
return (hostname_char_set_);
}
/// @brief Sets new hostname-char-set
///
/// @param hostname_char_set New value to use.
void setHostnameCharSet(const util::Optional<std::string>& hostname_char_set) {
hostname_char_set_ = hostname_char_set;
}
/// @brief Return the invalid char replacement used to sanitize client hostnames.
util::Optional<std::string>
getHostnameCharReplacement() const {
return (hostname_char_replacement_);
}
/// @brief Sets new hostname-char-replacement
///
/// @param hostname_char_replacement New value to use.
void setHostnameCharReplacement(const util::Optional<std::string>&
hostname_char_replacement) {
hostname_char_replacement_ = hostname_char_replacement;
}
/// @brief Checks if any of the DDNS parameters has a value.
///
/// @return True if any of the DDNS parameters are specified.
bool hasDdnsParameters();
protected:
/// @brief protected constructor
@@ -228,6 +423,52 @@ protected:
/// @brief Holds pool-specific allocation state.
AllocationStatePtr allocation_state_;
/// @brief Should Kea perform DNS updates. Used to provide scoped enabling
/// and disabling of updates.
util::Optional<bool> ddns_send_updates_;
/// @brief Should Kea perform updates, even if client requested no updates.
/// Overrides the client request for no updates via the N flag.
util::Optional<bool> ddns_override_no_update_;
/// @brief Should Kea perform updates, even if client requested delegation.
util::Optional<bool> ddns_override_client_update_;
/// @brief How Kea should handle the domain-name supplied by the client.
util::Optional<D2ClientConfig::ReplaceClientNameMode> ddns_replace_client_name_mode_;
/// @brief Prefix Kea should use when generating domain-names.
util::Optional<std::string> ddns_generated_prefix_;
/// @brief Suffix Kea should use when to qualify partial domain-names.
util::Optional<std::string> ddns_qualifying_suffix_;
/// @brief Should Kea perform updates when leases are extended
util::Optional<bool> ddns_update_on_renew_;
/// @brief DDNS conflict resolution mode
util::Optional<std::string> ddns_conflict_resolution_mode_;
/// @brief Percentage of the lease lifetime to use for DNS TTL.
util::Optional<double> ddns_ttl_percent_;
/// @brief Explicit value to use for DNS TTL.
util::Optional<uint32_t> ddns_ttl_;
/// @brief Minimum value to use for DNS TTL.
util::Optional<uint32_t> ddns_ttl_min_;
/// @brief Maximum value to use for DNS TTL.
util::Optional<uint32_t> ddns_ttl_max_;
/// @brief Regular expression describing invalid characters for client
/// hostnames.
util::Optional<std::string> hostname_char_set_;
/// @brief A string to replace invalid characters when scrubbing hostnames.
/// Meaningful only if hostname_char_set_ is not empty.
util::Optional<std::string> hostname_char_replacement_;
};
class Pool4;

View File

@@ -977,6 +977,13 @@ DdnsParams::getEnableUpdates() const {
return (false);
}
if (pool_) {
auto optional = pool_->getDdnsSendUpdates();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (d2_client_enabled_ && subnet_->getDdnsSendUpdates().get());
}
@@ -986,6 +993,13 @@ DdnsParams::getOverrideNoUpdate() const {
return (false);
}
if (pool_) {
auto optional = pool_->getDdnsOverrideNoUpdate();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsOverrideNoUpdate().get());
}
@@ -994,6 +1008,13 @@ bool DdnsParams::getOverrideClientUpdate() const {
return (false);
}
if (pool_) {
auto optional = pool_->getDdnsOverrideClientUpdate();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsOverrideClientUpdate().get());
}
@@ -1003,6 +1024,13 @@ DdnsParams::getReplaceClientNameMode() const {
return (D2ClientConfig::RCM_NEVER);
}
if (pool_) {
auto optional = pool_->getDdnsReplaceClientNameMode();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsReplaceClientNameMode().get());
}
@@ -1012,6 +1040,13 @@ DdnsParams::getGeneratedPrefix() const {
return ("");
}
if (pool_) {
auto optional = pool_->getDdnsGeneratedPrefix();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsGeneratedPrefix().get());
}
@@ -1021,6 +1056,13 @@ DdnsParams::getQualifyingSuffix() const {
return ("");
}
if (pool_) {
auto optional = pool_->getDdnsQualifyingSuffix();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsQualifyingSuffix().get());
}
@@ -1103,15 +1145,46 @@ DdnsParams::getUpdateOnRenew() const {
return (false);
}
if (pool_) {
auto optional = pool_->getDdnsUpdateOnRenew();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsUpdateOnRenew().get());
}
std::string
DdnsParams::getConflictResolutionMode() const {
if (!subnet_) {
return ("check-with-dhcid");
}
if (pool_) {
auto optional = pool_->getDdnsConflictResolutionMode();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsConflictResolutionMode().get());
}
util::Optional<double>
DdnsParams::getTtlPercent() const {
if (!subnet_) {
return (util::Optional<double>());
}
if (pool_) {
auto optional = pool_->getDdnsTtlPercent();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsTtlPercent());
}
@@ -1121,6 +1194,13 @@ DdnsParams::getTtl() const {
return (util::Optional<uint32_t>());
}
if (pool_) {
auto optional = pool_->getDdnsTtl();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsTtl());
}
@@ -1130,6 +1210,13 @@ DdnsParams::getTtlMin() const {
return (util::Optional<uint32_t>());
}
if (pool_) {
auto optional = pool_->getDdnsTtlMin();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsTtlMin());
}
@@ -1139,17 +1226,28 @@ DdnsParams::getTtlMax() const {
return (util::Optional<uint32_t>());
}
if (pool_) {
auto optional = pool_->getDdnsTtlMax();
if (!optional.unspecified()) {
return (optional.get());
}
}
return (subnet_->getDdnsTtlMax());
}
std::string
DdnsParams::getConflictResolutionMode() const {
PoolPtr
DdnsParams::setPoolFromAddress(const asiolink::IOAddress& address) {
if (!subnet_) {
return ("check-with-dhcid");
/// @todo Not sure this can happen.
isc_throw(InvalidOperation,
"DdnsParams::setPoolFromAddress called without a subnet");
}
return (subnet_->getDdnsConflictResolutionMode().get());
pool_ = subnet_->getPool((address.isV4() ? Lease::TYPE_V4 : Lease::TYPE_NA), address, false);
return (pool_);
}
} // namespace dhcp
} // namespace isc

View File

@@ -51,26 +51,15 @@ public:
/// @brief Default constructor
DdnsParams() : subnet_(), d2_client_enabled_(false) {}
/// @brief Constructor for DHCPv4 subnets
/// @brief Constructor
///
/// @param subnet Pointer to Subnet4 instance to use for fetching
/// @param subnet Pointer to subnet instance to use for fetching
/// parameter values (typically this is the selected subnet).
/// @param d2_client_enabled flag which indicates whether or not
/// D2Client is enabled (typically the value should come from
/// global D2Client configuration).
DdnsParams(const ConstSubnet4Ptr& subnet, bool d2_client_enabled)
: subnet_(boost::dynamic_pointer_cast<const Subnet>(subnet)),
d2_client_enabled_(d2_client_enabled) {}
/// @brief Constructor for DHCPv6 subnets
///
/// @param subnet Pointer to Subnet6 instance to use for fetching
/// parameter values (typically this is the selected subnet).
/// @param d2_client_enabled flag which indicates whether or not
/// D2Client is enabled (typically the value should come from
/// global D2Client configuration).
DdnsParams(const ConstSubnet6Ptr& subnet, bool d2_client_enabled)
: subnet_(boost::dynamic_pointer_cast<const Subnet>(subnet)),
DdnsParams(const ConstSubnetPtr& subnet, bool d2_client_enabled)
: subnet_(subnet),
d2_client_enabled_(d2_client_enabled) {}
/// @brief Returns whether or not DHCP DDNS updating is enabled.
@@ -192,12 +181,25 @@ public:
}
}
PoolPtr setPoolFromAddress(const asiolink::IOAddress& address);
void resetPool() {
pool_.reset();
}
PoolPtr getPool() const {
return (pool_);
}
private:
/// @brief Subnet from which values should be fetched.
ConstSubnetPtr subnet_;
/// @brief Flag indicating whether or not the D2Client is enabled.
bool d2_client_enabled_;
/// @brief Pool within the subnet from which values should be fetched.
PoolPtr pool_;
};
/// @brief Defines a pointer for DdnsParams instances.

View File

@@ -25,6 +25,131 @@ using namespace isc::asiolink;
namespace {
/// @brief Class for testing pools.
class PoolTest : public ::testing::Test {
public:
/// @brief Constructor
PoolTest() = default;
/// @brief Destructor
virtual ~PoolTest() = default;
/// @brief Verifies the DDNS parameter accessors and the
/// hasDdnsParameters() method.
///
/// @param family sets the protocol to be used AF_INET or AF_INET6.
void checkDdnsParamters(uint16_t family) {
PoolPtr pool;
if (family == AF_INET) {
pool.reset(new Pool4(IOAddress("192.0.2.0"), 25));
} else {
pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8::1"),
IOAddress("2001:db8::2")));
}
EXPECT_FALSE(pool->hasDdnsParameters());
util::Optional<bool> bool_unspec;
util::Optional<bool> bool_spec(true);
pool->setDdnsSendUpdates(bool_spec);
EXPECT_EQ(pool->getDdnsSendUpdates(), bool_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsSendUpdates(bool_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setDdnsOverrideNoUpdate(bool_spec);
EXPECT_EQ(pool->getDdnsOverrideNoUpdate(), bool_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsOverrideNoUpdate(bool_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setDdnsOverrideClientUpdate(bool_spec);
EXPECT_EQ(pool->getDdnsOverrideClientUpdate(), bool_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsOverrideClientUpdate(bool_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
util::Optional<D2ClientConfig::ReplaceClientNameMode> mode_unspec;
util::Optional<D2ClientConfig::ReplaceClientNameMode>
mode_spec(D2ClientConfig::RCM_WHEN_PRESENT);
pool->setDdnsReplaceClientNameMode(mode_spec);
EXPECT_EQ(pool->getDdnsReplaceClientNameMode(), mode_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsReplaceClientNameMode(mode_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
util::Optional<std::string> string_unspec;
util::Optional<std::string> string_spec("some_string");
pool->setDdnsGeneratedPrefix(string_spec);
EXPECT_EQ(pool->getDdnsGeneratedPrefix(), string_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsGeneratedPrefix(string_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setDdnsQualifyingSuffix(string_spec);
EXPECT_EQ(pool->getDdnsQualifyingSuffix(), string_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsQualifyingSuffix(string_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setDdnsUpdateOnRenew(bool_spec);
EXPECT_EQ(pool->getDdnsUpdateOnRenew(), bool_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsUpdateOnRenew(bool_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setDdnsConflictResolutionMode(string_spec);
EXPECT_EQ(pool->getDdnsConflictResolutionMode(), string_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsConflictResolutionMode(string_unspec);
util::Optional<double> double_unspec;
util::Optional<double> double_spec(0.5);
pool->setDdnsTtlPercent(double_spec);
EXPECT_EQ(pool->getDdnsTtlPercent(), double_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsTtlPercent(double_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
util::Optional<uint32_t> int_unspec;
util::Optional<uint32_t> int_spec(750);
pool->setDdnsTtl(int_spec);
EXPECT_EQ(pool->getDdnsTtl(), int_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsTtl(int_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setDdnsTtlMin(int_spec);
EXPECT_EQ(pool->getDdnsTtlMin(), int_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsTtlMin(int_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setDdnsTtlMax(int_spec);
EXPECT_EQ(pool->getDdnsTtlMax(), int_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setDdnsTtlMax(int_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setHostnameCharSet(string_spec);
EXPECT_EQ(pool->getHostnameCharSet(), string_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setHostnameCharSet(string_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
pool->setHostnameCharReplacement(string_spec);
EXPECT_EQ(pool->getHostnameCharReplacement(), string_spec);
EXPECT_TRUE(pool->hasDdnsParameters());
pool->setHostnameCharReplacement(string_unspec);
EXPECT_FALSE(pool->hasDdnsParameters());
}
};
TEST(Pool4Test, constructorFirstLast) {
// let's construct 192.0.2.1-192.0.2.255 pool
@@ -127,6 +252,45 @@ TEST(Pool4Test, toElement) {
" \"pool-id\": 5 "
"}";
isc::test::runToElementTest<Pool4>(expected3, pool3);
pool3.setDdnsSendUpdates(true);
pool3.setDdnsOverrideNoUpdate(true);
pool3.setDdnsOverrideClientUpdate(true);
pool3.setDdnsReplaceClientNameMode(D2ClientConfig::RCM_NEVER);
pool3.setDdnsGeneratedPrefix("prefix");
pool3.setDdnsQualifyingSuffix("example.com.");
pool3.setDdnsUpdateOnRenew(false);
pool3.setDdnsConflictResolutionMode("check-without-dhcid");
pool3.setDdnsTtlPercent(0.85);
pool3.setDdnsTtl(400);
pool3.setDdnsTtlMin(150);
pool3.setDdnsTtlMax(650);
pool3.setHostnameCharReplacement("x");
pool3.setHostnameCharSet("[^A-Z]");
std::string expected4 = R"(
{
"pool": "192.0.2.0/25",
"option-data": [ ],
"pool-id": 5,
"ddns-send-updates": true,
"ddns-override-no-update": true,
"ddns-override-client-update": true,
"ddns-replace-client-name": "never",
"ddns-generated-prefix": "prefix",
"ddns-qualifying-suffix": "example.com.",
"ddns-update-on-renew": false,
"ddns-conflict-resolution-mode": "check-without-dhcid",
"ddns-ttl": 400,
"ddns-ttl-max": 650,
"ddns-ttl-min": 150,
"ddns-ttl-percent": 0.85,
"hostname-char-replacement": "x",
"hostname-char-set": "[^A-Z]"
})";
isc::test::runToElementTest<Pool4>(expected4, pool3);
}
// This test checks that it is possible to specify pool specific options.
@@ -669,4 +833,12 @@ TEST(Pool6Test, additionalClasses) {
EXPECT_TRUE(pool.getAdditionalClasses().contains("foo"));
}
TEST_F(PoolTest, ddnsParameters4) {
checkDdnsParamters(AF_INET);
}
TEST_F(PoolTest, ddnsParameters6) {
checkDdnsParamters(AF_INET6);
}
} // end of anonymous namespace

View File

@@ -2283,4 +2283,231 @@ TEST_F(SrvConfigTest, sanityChecksDdnsTtlParameters) {
}
}
/// @brief Class for DdnsParams class.
class DdnsParamsTest : public testing::Test {
public:
/// @brief Constructor
DdnsParamsTest() = default;
/// @brief Destructor
virtual ~DdnsParamsTest() = default;
/// @brief Verifies that the DdnsParams accessors return parameter values
/// from the proper source.
///
/// @param subnet Subnet under test.
/// @param address Address to locate the pool within the subnet via
/// DdnsParams::setPoolFromAddress().
/// @param expected_pool expected Pool returned by setPoolFromAddress().
void checkDdnsParameters(SubnetPtr subnet, IOAddress address, PoolPtr expected_pool) {
// Create DdnsParams instance with the subnet.
DdnsParamsPtr params(new DdnsParams(subnet, true));
// Attempt to locate the pool based on the given address.
PoolPtr pool = params->setPoolFromAddress(address);
if (!expected_pool) {
// Pool should not have been found.
ASSERT_FALSE(pool);
} else {
// Pool should have been found.
ASSERT_TRUE(pool);
}
// Verify each of the parameters comes from the expected source, either
// the subnet or the pool.
if (pool && !(pool->getDdnsSendUpdates().unspecified())) {
EXPECT_EQ(params->getEnableUpdates(), pool->getDdnsSendUpdates().get());
} else {
EXPECT_EQ(params->getEnableUpdates(), subnet->getDdnsSendUpdates().get());
}
if (pool && !(pool->getDdnsOverrideNoUpdate().unspecified())) {
EXPECT_EQ(params->getOverrideNoUpdate(), pool->getDdnsOverrideNoUpdate().get());
} else {
EXPECT_EQ(params->getOverrideNoUpdate(), subnet->getDdnsOverrideNoUpdate().get());
}
if (pool && !(pool->getDdnsOverrideClientUpdate().unspecified())) {
EXPECT_EQ(params->getOverrideClientUpdate(), pool->getDdnsOverrideClientUpdate().get());
} else {
EXPECT_EQ(params->getOverrideClientUpdate(), subnet->getDdnsOverrideClientUpdate().get());
}
if (pool && !(pool->getDdnsReplaceClientNameMode().unspecified())) {
EXPECT_EQ(params->getReplaceClientNameMode(), pool->getDdnsReplaceClientNameMode().get());
} else {
EXPECT_EQ(params->getReplaceClientNameMode(), subnet->getDdnsReplaceClientNameMode().get());
}
if (pool && !(pool->getDdnsGeneratedPrefix().unspecified())) {
EXPECT_EQ(params->getGeneratedPrefix(), pool->getDdnsGeneratedPrefix().get());
} else {
EXPECT_EQ(params->getGeneratedPrefix(), subnet->getDdnsGeneratedPrefix().get());
}
if (pool && !(pool->getDdnsQualifyingSuffix().unspecified())) {
EXPECT_EQ(params->getQualifyingSuffix(), pool->getDdnsQualifyingSuffix().get());
} else {
EXPECT_EQ(params->getQualifyingSuffix(), subnet->getDdnsQualifyingSuffix().get());
}
if (pool && !(pool->getDdnsUpdateOnRenew().unspecified())) {
EXPECT_EQ(params->getUpdateOnRenew(), pool->getDdnsUpdateOnRenew().get());
} else {
EXPECT_EQ(params->getUpdateOnRenew(), subnet->getDdnsUpdateOnRenew().get());
}
if (pool && !(pool->getDdnsConflictResolutionMode().unspecified())) {
EXPECT_EQ(params->getConflictResolutionMode(), pool->getDdnsConflictResolutionMode().get());
} else {
EXPECT_EQ(params->getConflictResolutionMode(), subnet->getDdnsConflictResolutionMode().get());
}
if (pool && !(pool->getDdnsTtlPercent().unspecified())) {
EXPECT_EQ(params->getTtlPercent(), pool->getDdnsTtlPercent().get());
} else {
EXPECT_EQ(params->getTtlPercent(), subnet->getDdnsTtlPercent().get());
}
if (pool && !(pool->getDdnsTtl().unspecified())) {
EXPECT_EQ(params->getTtl(), pool->getDdnsTtl().get());
} else {
EXPECT_EQ(params->getTtl(), subnet->getDdnsTtl().get());
}
if (pool && !(pool->getDdnsTtlMin().unspecified())) {
EXPECT_EQ(params->getTtlMin(), pool->getDdnsTtlMin().get());
} else {
EXPECT_EQ(params->getTtlMin(), subnet->getDdnsTtlMin().get());
}
if (pool && !(pool->getDdnsTtlMax().unspecified())) {
EXPECT_EQ(params->getTtlMax(), pool->getDdnsTtlMax().get());
} else {
EXPECT_EQ(params->getTtlMax(), subnet->getDdnsTtlMax().get());
}
}
};
TEST_F(DdnsParamsTest, checkDdnsParameters4) {
// Create a subnet.
Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 10));
// Set values in the subnet for each of the DDNS parameters.
subnet->setDdnsSendUpdates(false);
subnet->setDdnsOverrideNoUpdate(true);
subnet->setDdnsOverrideClientUpdate(true);
subnet->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_ALWAYS);
subnet->setDdnsGeneratedPrefix("sn_prefix");
subnet->setDdnsQualifyingSuffix("sn_suffix");
subnet->setDdnsUpdateOnRenew(true);
subnet->setDdnsConflictResolutionMode("check-with-dhcid");
subnet->setDdnsTtlPercent(0.75);
subnet->setDdnsTtl(500);
subnet->setDdnsTtlMin(250);
subnet->setDdnsTtlMax(750);
subnet->setHostnameCharReplacement("X");
subnet->setHostnameCharSet("[^A-Z]");
// Create a pool and add it to the subnet.
Pool4Ptr pool(new Pool4(IOAddress("192.0.2.1"), IOAddress("192.0.2.100")));
ASSERT_NO_THROW(subnet->addPool(pool));
{
// Values all come from subnet, address not in pool.
SCOPED_TRACE("Address not in pool");
checkDdnsParameters(subnet, IOAddress("192.0.2.200"), Pool4Ptr());
}
{
// Values all come from subnet, address in pool but pool specifies no values.
SCOPED_TRACE("Pool exists but specifies none");
checkDdnsParameters(subnet, IOAddress("192.0.2.50"), pool);
}
// Set values in the pool for each of the DDNS parameters.
pool->setDdnsSendUpdates(true);
pool->setDdnsOverrideNoUpdate(false);
pool->setDdnsOverrideClientUpdate(false);
pool->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_NEVER);
pool->setDdnsGeneratedPrefix("pl_prefix");
pool->setDdnsQualifyingSuffix("pl_suffix");
pool->setDdnsUpdateOnRenew(false);
pool->setDdnsConflictResolutionMode("check-without-dhcid");
pool->setDdnsTtlPercent(0.85);
pool->setDdnsTtl(400);
pool->setDdnsTtlMin(150);
pool->setDdnsTtlMax(650);
pool->setHostnameCharReplacement("y");
pool->setHostnameCharSet("[^a-z]");
{
// Values all come from pool.
SCOPED_TRACE("Pool specifies all");
checkDdnsParameters(subnet, IOAddress("192.0.2.50"), pool);
}
}
TEST_F(DdnsParamsTest, checkDdnsParameters6) {
// Create a subnet.
Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64,
1, 2, 3, 4, SubnetID(1)));
// Set values in the subnet for each of the DDNS parameters.
subnet->setDdnsSendUpdates(false);
subnet->setDdnsOverrideNoUpdate(true);
subnet->setDdnsOverrideClientUpdate(true);
subnet->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_ALWAYS);
subnet->setDdnsGeneratedPrefix("sn_prefix");
subnet->setDdnsQualifyingSuffix("sn_suffix");
subnet->setDdnsUpdateOnRenew(true);
subnet->setDdnsConflictResolutionMode("check-with-dhcid");
subnet->setDdnsTtlPercent(0.75);
subnet->setDdnsTtl(500);
subnet->setDdnsTtlMin(250);
subnet->setDdnsTtlMax(750);
subnet->setHostnameCharReplacement("X");
subnet->setHostnameCharSet("[^A-Z]");
// Create a pool and add it to the subnet.
Pool6Ptr pool(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"),
IOAddress("2001:db8:1::100")));
ASSERT_NO_THROW(subnet->addPool(pool));
{
// Values all come from subnet, address not in pool.
SCOPED_TRACE("Address not in pool");
checkDdnsParameters(subnet, IOAddress("2001:db8:1::200"), Pool6Ptr());
}
{
// Values all come from subnet, address in pool but pool specifies no values.
SCOPED_TRACE("Pool exists but specifies none");
checkDdnsParameters(subnet, IOAddress("2001:db8:1::10"), pool);
}
// Set values in the pool for each of the DDNS parameters.
pool->setDdnsSendUpdates(true);
pool->setDdnsOverrideNoUpdate(false);
pool->setDdnsOverrideClientUpdate(false);
pool->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_NEVER);
pool->setDdnsGeneratedPrefix("pl_prefix");
pool->setDdnsQualifyingSuffix("pl_suffix");
pool->setDdnsUpdateOnRenew(false);
pool->setDdnsConflictResolutionMode("check-without-dhcid");
pool->setDdnsTtlPercent(0.85);
pool->setDdnsTtl(400);
pool->setDdnsTtlMin(150);
pool->setDdnsTtlMax(650);
pool->setHostnameCharReplacement("y");
pool->setHostnameCharSet("[^a-z]");
{
// Values all come from pool.
SCOPED_TRACE("Pool specifies all");
checkDdnsParameters(subnet, IOAddress("2001:db8:1::10"), pool);
}
}
} // end of anonymous namespace