]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3049] Added params to Pool, modified DdnsParams
authorThomas Markwalder <tmark@isc.org>
Fri, 20 Dec 2024 17:19:42 +0000 (12:19 -0500)
committerThomas Markwalder <tmark@isc.org>
Wed, 22 Jan 2025 15:24:25 +0000 (15:24 +0000)
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

src/bin/dhcp4/dhcp4_srv.cc
src/lib/dhcpsrv/ncr_generator.cc
src/lib/dhcpsrv/network.h
src/lib/dhcpsrv/pool.cc
src/lib/dhcpsrv/pool.h
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/srv_config.h
src/lib/dhcpsrv/tests/pool_unittest.cc
src/lib/dhcpsrv/tests/srv_config_unittest.cc

index 2f7b76784fde18b7505e56613bf91448279283ce..435407d2258665bacce557c03ff0811e9dc720fd 100644 (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.
-
-            // 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;
-
-            // 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;
-            }
+            reprocess_client_name = true;
+        }
+    }
+
+    // Tracks whether or not the client name (FQDN or host) has changed since
+    // the lease was allocated.
+    bool client_name_changed = false;
+
+    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;
+
+        // 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;
         }
     }
 
index 61540026e5cb413f80a89864b2ba1ae76da266d2..978d7dcc9c075c9f423b92175d2d05b5e4ff7bda 100644 (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);
-         }
-
-         ddns_ttl_percent = subnet->getDdnsTtlPercent();
-         ddns_ttl = subnet->getDdnsTtl();
-         ddns_ttl_min = subnet->getDdnsTtlMin();
-         ddns_ttl_max = subnet->getDdnsTtlMax();
-     }
+    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_));
+
+        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);
index 69af817f3f42f5bf7676d3814d3bc364190d4c40..c60470e6b730ac8e853a781d614d1ee1ae05839f 100644 (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) {
index 2c80084e6efcd8d2802836e1f7ab9f4a86b53d66..904ee30eb50ccbecaae1bf657b4812d9b89a6f04 100644 (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);
 }
 
index 7d2ad92090c27d64fd55b03e7f8063da106cba7d..a6286a4ae6c590509f04056268c61ffdbe4a01c5 100644 (file)
 #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;
index bbb7d37eebc521787c093c1554c35cabaf244f8e..24defcf9a800755a3c79349a6ff0145ac698ced7 100644 (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
index 53c275f6d72313161c02f1d010534efbf7bbc5a7..4913e1c60bd1645560ffc8dc8107db43838bbce4 100644 (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.
index a3424a3fd949c06e6bf2170ba05f1a4a5cb59d2d..044afbffed5df67f3d6b6b8005c93db6a1b5925d 100644 (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
index 100660da86eaf5b811635e5285e5ba6f06d6baad..a804be647aa480f06287981ae18d3facf58315af 100644 (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