BIND 10 master, updated. 42b1f1e4c4f5aa48b7588233402876f5012c043c [master] Merge branch 'trac3282'
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Jan 30 15:07:40 UTC 2014
The branch, master has been updated
via 42b1f1e4c4f5aa48b7588233402876f5012c043c (commit)
via 587c50528ab9c42467fc03327dfdbadd8933e360 (commit)
via d38e7638f9ecafc4e0db3a6b9dcad7a808c49781 (commit)
via f9cea380de68d0c181b4f80ad3df05cd53eb37e9 (commit)
via c75896605bb0224250ff5a460d4bb7de55e18751 (commit)
via 4289cec8c39d8ed8411c1b2718c5d8f63aa3f122 (commit)
via 8534089c1e8a7e3c48a49bf08784ebf05d11b3a1 (commit)
via 7f2ef4108043f94170a153174d06323fa2ce9653 (commit)
via 1422c7b03ba6d90ce0b8e0c6a02e6cbbe68c7a9e (commit)
from f6e165a5736ffd3be849ffa2a6459fcf77dd5296 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 42b1f1e4c4f5aa48b7588233402876f5012c043c
Merge: f6e165a 587c505
Author: Thomas Markwalder <tmark at isc.org>
Date: Thu Jan 30 09:48:32 2014 -0500
[master] Merge branch 'trac3282'
Implements use of dhcp-ddns configuration parameters in b10-dhcp4.
-----------------------------------------------------------------------
Summary of changes:
src/bin/dhcp4/dhcp4.spec | 7 -
src/bin/dhcp4/dhcp4_srv.cc | 205 +++-----
src/bin/dhcp4/tests/config_parser_unittest.cc | 3 -
src/bin/dhcp4/tests/fqdn_unittest.cc | 356 ++++++++++---
src/lib/dhcp/option4_client_fqdn.cc | 5 +-
src/lib/dhcp/option6_client_fqdn.cc | 5 +-
src/lib/dhcp/tests/option4_client_fqdn_unittest.cc | 3 +-
src/lib/dhcp/tests/option6_client_fqdn_unittest.cc | 3 +-
src/lib/dhcpsrv/cfgmgr.cc | 6 +-
src/lib/dhcpsrv/cfgmgr.h | 9 +-
src/lib/dhcpsrv/d2_client.cc | 104 +++-
src/lib/dhcpsrv/d2_client.h | 142 ++++-
src/lib/dhcpsrv/dhcp_parsers.cc | 13 +-
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc | 14 +-
src/lib/dhcpsrv/tests/d2_client_unittest.cc | 556 +++++++++++++++++++-
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc | 20 +-
16 files changed, 1133 insertions(+), 318 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index c6d2c32..04bcc7a 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -336,13 +336,6 @@
"item_description" : "Format of the update request packet"
},
{
- "item_name": "remove-on-renew",
- "item_type": "boolean",
- "item_optional": true,
- "item_default": false,
- "item_description": "Enable requesting a DNS Remove, before a DNS Update on renewals"
- },
- {
"item_name": "always-include-fqdn",
"item_type": "boolean",
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 5fc5598..48f6047 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -80,36 +80,6 @@ Dhcp4Hooks Hooks;
namespace isc {
namespace dhcp {
-namespace {
-
-// @todo The following constants describe server's behavior with respect to the
-// DHCPv4 Client FQDN Option sent by a client. They will be removed
-// when DDNS parameters for DHCPv4 are implemented with the ticket #3033.
-
-// @todo Additional configuration parameter which we may consider is the one
-// that controls whether the DHCP server sends the removal NameChangeRequest
-// if it discovers that the entry for the particular client exists or that
-// it always updates the DNS.
-
-// Should server always include the FQDN option in its response, regardless
-// if it has been requested in Parameter Request List Option (Disabled).
-const bool FQDN_ALWAYS_INCLUDE = false;
-// Enable A RR update delegation to the client (Disabled).
-const bool FQDN_ALLOW_CLIENT_UPDATE = false;
-// Globally enable updates (Enabled).
-const bool FQDN_ENABLE_UPDATE = true;
-// Do update, even if client requested no updates with N flag (Disabled).
-const bool FQDN_OVERRIDE_NO_UPDATE = false;
-// Server performs an update when client requested delegation (Enabled).
-const bool FQDN_OVERRIDE_CLIENT_UPDATE = true;
-// The fully qualified domain-name suffix if partial name provided by
-// a client.
-const char* FQDN_PARTIAL_SUFFIX = "example.com";
-// Should server replace the domain-name supplied by the client (Disabled).
-const bool FQDN_REPLACE_CLIENT_NAME = false;
-
-}
-
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
const bool direct_response_desired)
: shutdown_(true), alloc_engine_(), port_(port),
@@ -596,8 +566,8 @@ Dhcpv4Srv::appendServerID(const Pkt4Ptr& response) {
// The source address for the outbound message should have been set already.
// This is the address that to the best of the server's knowledge will be
// available from the client.
- // @todo: perhaps we should consider some more sophisticated server id
- // generation, but for the current use cases, it should be ok.
+ /// @todo: perhaps we should consider some more sophisticated server id
+ /// generation, but for the current use cases, it should be ok.
response->addOption(OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
response->getLocalAddr()))
);
@@ -666,8 +636,8 @@ Dhcpv4Srv::appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer
uint32_t vendor_id = vendor_req->getVendorId();
// Let's try to get ORO within that vendor-option
- /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other vendors
- /// may have different policies.
+ /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other
+ /// vendors may have different policies.
OptionUint8ArrayPtr oro =
boost::dynamic_pointer_cast<OptionUint8Array>(vendor_req->getOption(DOCSIS3_V4_ORO));
@@ -773,68 +743,19 @@ Dhcpv4Srv::processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
// response to a client.
Option4ClientFqdnPtr fqdn_resp(new Option4ClientFqdn(*fqdn));
- // RFC4702, section 4 - set 'NOS' flags to 0.
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_S, 0);
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_O, 0);
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_N, 0);
-
- // Conditions when N flag has to be set to indicate that server will not
- // perform DNS updates:
- // 1. Updates are globally disabled,
- // 2. Client requested no update and server respects it,
- // 3. Client requested that the forward DNS update is delegated to the
- // client but server neither respects requests for forward update
- // delegation nor it is configured to send update on its own when
- // client requested delegation.
- if (!FQDN_ENABLE_UPDATE ||
- (fqdn->getFlag(Option4ClientFqdn::FLAG_N) &&
- !FQDN_OVERRIDE_NO_UPDATE) ||
- (!fqdn->getFlag(Option4ClientFqdn::FLAG_S) &&
- !FQDN_ALLOW_CLIENT_UPDATE && !FQDN_OVERRIDE_CLIENT_UPDATE)) {
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_N, true);
-
- // Conditions when S flag is set to indicate that server will perform DNS
- // update on its own:
- // 1. Client requested that server performs DNS update and DNS updates are
- // globally enabled.
- // 2. Client requested that server delegates forward update to the client
- // but server doesn't respect requests for delegation and it is
- // configured to perform an update on its own when client requested the
- // delegation.
- } else if (fqdn->getFlag(Option4ClientFqdn::FLAG_S) ||
- (!fqdn->getFlag(Option4ClientFqdn::FLAG_S) &&
- !FQDN_ALLOW_CLIENT_UPDATE && FQDN_OVERRIDE_CLIENT_UPDATE)) {
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_S, true);
- }
-
- // Server MUST set the O flag if it has overriden the client's setting
- // of S flag.
- if (fqdn->getFlag(Option4ClientFqdn::FLAG_S) !=
- fqdn_resp->getFlag(Option4ClientFqdn::FLAG_S)) {
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_O, true);
- }
+ // Set the server S, N, and O flags based on client's flags and
+ // current configuration.
+ D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
+ d2_mgr.adjustFqdnFlags<Option4ClientFqdn>(*fqdn, *fqdn_resp);
- // If client suppled partial or empty domain-name, server should generate
- // one.
- if (fqdn->getDomainNameType() == Option4ClientFqdn::PARTIAL) {
- std::ostringstream name;
- if (fqdn->getDomainName().empty() || FQDN_REPLACE_CLIENT_NAME) {
- fqdn_resp->setDomainName("", Option4ClientFqdn::PARTIAL);
+ // Carry over the client's E flag.
+ fqdn_resp->setFlag(Option4ClientFqdn::FLAG_E,
+ fqdn->getFlag(Option4ClientFqdn::FLAG_E));
- } else {
- name << fqdn->getDomainName();
- name << "." << FQDN_PARTIAL_SUFFIX;
- fqdn_resp->setDomainName(name.str(), Option4ClientFqdn::FULL);
- }
-
- // Server may be configured to replace a name supplied by a client, even if
- // client supplied fully qualified domain-name. The empty domain-name is
- // is set to indicate that the name must be generated when the new lease
- // is acquired.
- } else if(FQDN_REPLACE_CLIENT_NAME) {
- fqdn_resp->setDomainName("", Option4ClientFqdn::PARTIAL);
- }
+ // Adjust the domain name based on domain name value and type sent by the
+ // client and current configuration.
+ d2_mgr.adjustDomainName<Option4ClientFqdn>(*fqdn, *fqdn_resp);
// Add FQDN option to the response message. Note that, there may be some
// cases when server may choose not to include the FQDN option in a
@@ -854,15 +775,20 @@ Dhcpv4Srv::processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
void
Dhcpv4Srv::processHostnameOption(const OptionCustomPtr& opt_hostname,
Pkt4Ptr& answer) {
+ // Fetch D2 configuration.
+ D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
+
// Do nothing if the DNS updates are disabled.
- if (!FQDN_ENABLE_UPDATE) {
+ if (!d2_mgr.ddnsEnabled()) {
return;
}
std::string hostname = isc::util::str::trim(opt_hostname->readString());
unsigned int label_count = OptionDataTypeUtil::getLabelCount(hostname);
// The hostname option sent by the client should be at least 1 octet long.
- // If it isn't we ignore this option.
+ // If it isn't we ignore this option. (Per RFC 2131, section 3.14)
+ /// @todo It would be more liberal to accept this and let it fall into
+ /// the case of replace or less than two below.
if (label_count == 0) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_EMPTY_HOSTNAME);
return;
@@ -880,21 +806,20 @@ Dhcpv4Srv::processHostnameOption(const OptionCustomPtr& opt_hostname,
// By checking the number of labels present in the hostname we may infer
// whether client has sent the fully qualified or unqualified hostname.
- // @todo We may want to reconsider whether it is appropriate for the
- // client to send a root domain name as a Hostname. There are
- // also extensions to the auto generation of the client's name,
- // e.g. conversion to the puny code which may be considered at some point.
- // For now, we just remain liberal and expect that the DNS will handle
- // conversion if needed and possible.
- if (FQDN_REPLACE_CLIENT_NAME || (label_count < 2)) {
+ /// @todo We may want to reconsider whether it is appropriate for the
+ /// client to send a root domain name as a Hostname. There are
+ /// also extensions to the auto generation of the client's name,
+ /// e.g. conversion to the puny code which may be considered at some point.
+ /// For now, we just remain liberal and expect that the DNS will handle
+ /// conversion if needed and possible.
+ if ((d2_mgr.getD2ClientConfig()->getReplaceClientName()) ||
+ (label_count < 2)) {
opt_hostname_resp->writeString("");
- // If there are two labels, it means that the client has specified
- // the unqualified name. We have to concatenate the unqalified name
- // with the domain name.
} else if (label_count == 2) {
- std::ostringstream resp_hostname;
- resp_hostname << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
- opt_hostname_resp->writeString(resp_hostname.str());
+ // If there are two labels, it means that the client has specified
+ // the unqualified name. We have to concatenate the unqalified name
+ // with the domain name.
+ opt_hostname_resp->writeString(d2_mgr.qualifyName(hostname));
}
answer->addOption(opt_hostname_resp);
@@ -985,9 +910,9 @@ queueNameChangeRequest(const isc::dhcp_ddns::NameChangeType chg_type,
void
Dhcpv4Srv::sendNameChangeRequests() {
while (!name_change_reqs_.empty()) {
- // @todo Once next NameChangeRequest is picked from the queue
- // we should send it to the b10-dhcp_ddns module. Currently we
- // just drop it.
+ /// @todo Once next NameChangeRequest is picked from the queue
+ /// we should send it to the b10-dhcp_ddns module. Currently we
+ /// just drop it.
name_change_reqs_.pop();
}
}
@@ -1006,8 +931,8 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// thing this client can get is some global information (like DNS
// servers).
- // perhaps this should be logged on some higher level? This is most likely
- // configuration bug.
+ // perhaps this should be logged on some higher level? This is most
+ // likely configuration bug.
LOG_ERROR(dhcp4_logger, DHCP4_SUBNET_SELECTION_FAILED)
.arg(question->getRemoteAddr().toText())
.arg(serverReceivedPacketName(question->getType()));
@@ -1020,7 +945,7 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// as siaddr has nothing to do with a lease, but otherwise we would have
// to select subnet twice (performance hit) or update too many functions
// at once.
- // @todo: move subnet selection to a common code
+ /// @todo: move subnet selection to a common code
answer->setSiaddr(subnet->getSiaddr());
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_SELECTED)
@@ -1063,8 +988,8 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
(answer->getOption(DHO_HOST_NAME));
if (opt_hostname) {
hostname = opt_hostname->readString();
- // @todo It could be configurable what sort of updates the server
- // is doing when Hostname option was sent.
+ /// @todo It could be configurable what sort of updates the
+ /// server is doing when Hostname option was sent.
fqdn_fwd = true;
fqdn_rev = true;
}
@@ -1074,7 +999,7 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// will try to honour the hint, but it is just a hint - some other address
// may be used instead. If fake_allocation is set to false, the lease will
// be inserted into the LeaseMgr as well.
- // @todo pass the actual FQDN data.
+ /// @todo pass the actual FQDN data.
Lease4Ptr old_lease;
Lease4Ptr lease = alloc_engine_->allocateLease4(subnet, client_id, hwaddr,
hint, fqdn_fwd, fqdn_rev,
@@ -1099,14 +1024,9 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// generating the entire hostname for the client. The example of the
// client's name, generated from the IP address is: host-192-0-2-3.
if ((fqdn || opt_hostname) && lease->hostname_.empty()) {
- hostname = lease->addr_.toText();
- // Replace dots with hyphens.
- std::replace(hostname.begin(), hostname.end(), '.', '-');
- ostringstream stream;
- // The partial suffix will need to be replaced with the actual
- // domain-name for the client when configuration is implemented.
- stream << "host-" << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
- lease->hostname_ = stream.str();
+ lease->hostname_ = CfgMgr::instance()
+ .getD2ClientMgr().generateFqdn(lease->addr_);
+
// The operations below are rather safe, but we want to catch
// any potential exceptions (e.g. invalid lease database backend
// implementation) and log an error.
@@ -1138,22 +1058,18 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// Subnet mask (type 1)
answer->addOption(getNetmaskOption(subnet));
- // @todo: send renew timer option (T1, option 58)
- // @todo: send rebind timer option (T2, option 59)
+ /// @todo: send renew timer option (T1, option 58)
+ /// @todo: send rebind timer option (T2, option 59)
- // @todo Currently the NameChangeRequests are always generated if
- // real (not fake) allocation is being performed. Should we have
- // control switch to enable/disable NameChangeRequest creation?
- // Perhaps we need a way to detect whether the b10-dhcp-ddns module
- // is up an running?
- if (!fake_allocation) {
+ // Create NameChangeRequests if DDNS is enabled and this is a
+ // real allocation.
+ if (!fake_allocation && CfgMgr::instance().ddnsEnabled()) {
try {
createNameChangeRequests(lease, old_lease);
} catch (const Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_NCR_CREATION_FAILED)
.arg(ex.what());
}
-
}
} else {
@@ -1194,8 +1110,8 @@ Dhcpv4Srv::adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response) {
// address for the response. Instead, we have to check what address our
// socket is bound to and use it as a source address. This operation
// may throw if for some reason the socket is closed.
- // @todo Consider an optimization that we use local address from
- // the query if this address is not broadcast.
+ /// @todo Consider an optimization that we use local address from
+ /// the query if this address is not broadcast.
SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
// Set local adddress, port and interface.
response->setLocalAddr(sock_info.addr_);
@@ -1310,7 +1226,7 @@ Pkt4Ptr
Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
/// @todo Uncomment this (see ticket #3116)
- // sanityCheck(request, MANDATORY);
+ /// sanityCheck(request, MANDATORY);
Pkt4Ptr ack = Pkt4Ptr
(new Pkt4(DHCPACK, request->getTransid()));
@@ -1352,7 +1268,7 @@ void
Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
/// @todo Uncomment this (see ticket #3116)
- // sanityCheck(release, MANDATORY);
+ /// sanityCheck(release, MANDATORY);
// Try to find client-id
ClientIdPtr client_id;
@@ -1377,7 +1293,7 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
// Does the hardware address match? We don't want one client releasing
// second client's leases.
if (lease->hwaddr_ != release->getHWAddr()->hwaddr_) {
- // @todo: Print hwaddr from lease as part of ticket #2589
+ /// @todo: Print hwaddr from lease as part of ticket #2589
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
.arg(release->getCiaddr().toText())
.arg(client_id ? client_id->toText() : "(no client-id)")
@@ -1435,9 +1351,10 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
.arg(client_id ? client_id->toText() : "(no client-id)")
.arg(release->getHWAddr()->toText());
- // Remove existing DNS entries for the lease, if any.
- queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, lease);
-
+ if (CfgMgr::instance().ddnsEnabled()) {
+ // Remove existing DNS entries for the lease, if any.
+ queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, lease);
+ }
} else {
// Release failed -
LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
@@ -1678,8 +1595,8 @@ Dhcpv4Srv::openActiveSockets(const uint16_t port,
}
// Let's reopen active sockets. openSockets4 will check internally whether
// sockets are marked active or inactive.
- // @todo Optimization: we should not reopen all sockets but rather select
- // those that have been affected by the new configuration.
+ /// @todo Optimization: we should not reopen all sockets but rather select
+ /// those that have been affected by the new configuration.
isc::dhcp::IfaceMgrErrorMsgCallback error_handler =
boost::bind(&Dhcpv4Srv::ifaceMgrSocket4ErrorHandler, _1);
if (!IfaceMgr::instance().openSockets4(port, use_bcast, error_handler)) {
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index 1af66c5..4071adc 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -2712,7 +2712,6 @@ TEST_F(Dhcp4ParserTest, d2ClientConfig) {
" \"server-port\" : 777, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"allow-client-update\" : true, "
" \"override-no-update\" : true, "
@@ -2745,7 +2744,6 @@ TEST_F(Dhcp4ParserTest, d2ClientConfig) {
EXPECT_EQ(777, d2_client_config->getServerPort());
EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
- EXPECT_TRUE(d2_client_config->getRemoveOnRenew());
EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
@@ -2772,7 +2770,6 @@ TEST_F(Dhcp4ParserTest, invalidD2ClientConfig) {
" \"server-port\" : 5301, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"allow-client-update\" : true, "
" \"override-no-update\" : true, "
diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc
index d3bf9ae..e17a7e3 100644
--- a/src/bin/dhcp4/tests/fqdn_unittest.cc
+++ b/src/bin/dhcp4/tests/fqdn_unittest.cc
@@ -18,6 +18,7 @@
#include <dhcp/option_int_array.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp_ddns/ncr_msg.h>
+#include <dhcpsrv/cfgmgr.h>
#include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp>
@@ -32,13 +33,53 @@ namespace {
class NameDhcpv4SrvTest : public Dhcpv4SrvFakeIfaceTest {
public:
+
+ // Bit Constants for turning on and off DDNS configuration options.
+ static const uint16_t ALWAYS_INCLUDE_FQDN = 1;
+ static const uint16_t OVERRIDE_NO_UPDATE = 2;
+ static const uint16_t OVERRIDE_CLIENT_UPDATE = 4;
+ static const uint16_t REPLACE_CLIENT_NAME = 8;
+
NameDhcpv4SrvTest() : Dhcpv4SrvFakeIfaceTest() {
srv_ = new NakedDhcpv4Srv(0);
+ // Config DDNS to be enabled, all controls off
+ enableD2();
}
+
virtual ~NameDhcpv4SrvTest() {
delete srv_;
}
+ /// @brief Sets the server's DDNS configuration to ddns updates disabled.
+ void disableD2() {
+ // Default constructor creates a config with DHCP-DDNS updates
+ // disabled.
+ D2ClientConfigPtr cfg(new D2ClientConfig());
+ CfgMgr::instance().setD2ClientConfig(cfg);
+ }
+
+ /// @brief Enables DHCP-DDNS updates with the given options enabled.
+ ///
+ /// Replaces the current D2ClientConfiguration with a configuration
+ /// which as updates enabled and the control options set based upon
+ /// the bit mask of options.
+ ///
+ /// @param mask Bit mask of configuration options that should be enabled.
+ void enableD2(const uint16_t mask = 0) {
+ D2ClientConfigPtr cfg;
+
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("192.0.2.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ (mask & ALWAYS_INCLUDE_FQDN),
+ (mask & OVERRIDE_NO_UPDATE),
+ (mask & OVERRIDE_CLIENT_UPDATE),
+ (mask & REPLACE_CLIENT_NAME),
+ "myhost", "example.com")));
+
+ CfgMgr::instance().setD2ClientConfig(cfg);
+ }
+
// Create a lease to be used by various tests.
Lease4Ptr createLease(const isc::asiolink::IOAddress& addr,
const std::string& hostname,
@@ -78,15 +119,18 @@ public:
return (opt_hostname);
}
- // Generates partial hostname from the address. The format of the
- // generated address is: host-A-B-C-D, where A.B.C.D is an IP
- // address.
+ /// @brief Convenience method for generating an FQDN from an IP address.
+ ///
+ /// This is just a wrapper method around the D2ClientMgr's method for
+ /// generating domain names from the configured prefix, suffix, and a
+ /// given IP address. This is useful for verifying that fully generated
+ /// names are correct.
+ ///
+ /// @param addr IP address used in the lease.
+ ///
+ /// @return An std::string contained the generated FQDN.
std::string generatedNameFromAddress(const IOAddress& addr) {
- std::string gen_name = addr.toText();
- std::replace(gen_name.begin(), gen_name.end(), '.', '-');
- std::ostringstream hostname;
- hostname << "host-" << gen_name;
- return (hostname.str());
+ return(CfgMgr::instance().getD2ClientMgr().generateFqdn(addr));
}
// Get the Client FQDN Option from the given message.
@@ -182,6 +226,21 @@ public:
Option4ClientFqdnPtr fqdn = getClientFqdnOption(answer);
ASSERT_TRUE(fqdn);
+ checkFqdnFlags(answer, exp_flags);
+
+ EXPECT_EQ(exp_domain_name, fqdn->getDomainName());
+ EXPECT_EQ(exp_domain_type, fqdn->getDomainNameType());
+
+ }
+
+ /// @brief Checks the packet's FQDN option flags against a given mask
+ ///
+ /// @param pkt IPv4 packet whose FQDN flags should be checked.
+ /// @param exp_flags Bit mask of flags that are expected to be true.
+ void checkFqdnFlags(const Pkt4Ptr& pkt, const uint8_t exp_flags) {
+ Option4ClientFqdnPtr fqdn = getClientFqdnOption(pkt);
+ ASSERT_TRUE(fqdn);
+
const bool flag_n = (exp_flags & Option4ClientFqdn::FLAG_N) != 0;
const bool flag_s = (exp_flags & Option4ClientFqdn::FLAG_S) != 0;
const bool flag_o = (exp_flags & Option4ClientFqdn::FLAG_O) != 0;
@@ -191,12 +250,9 @@ public:
EXPECT_EQ(flag_s, fqdn->getFlag(Option4ClientFqdn::FLAG_S));
EXPECT_EQ(flag_o, fqdn->getFlag(Option4ClientFqdn::FLAG_O));
EXPECT_EQ(flag_e, fqdn->getFlag(Option4ClientFqdn::FLAG_E));
-
- EXPECT_EQ(exp_domain_name, fqdn->getDomainName());
- EXPECT_EQ(exp_domain_type, fqdn->getDomainNameType());
-
}
+
// Processes the Hostname option in the client's message and returns
// the hostname option which would be sent to the client. It will
// throw NULL pointer if the hostname option is not to be included
@@ -221,8 +277,8 @@ public:
}
- // Test that the client message holding an FQDN is processed and the
- // NameChangeRequests are generated.
+ // Test that the client message holding an FQDN is processed and
+ // that the response packet is as expected.
void testProcessMessageWithFqdn(const uint8_t msg_type,
const std::string& hostname) {
Pkt4Ptr req = generatePktWithFqdn(msg_type, Option4ClientFqdn::FLAG_S |
@@ -287,6 +343,50 @@ public:
srv_->name_change_reqs_.pop();
}
+
+ /// @brief Tests processing a request with the given client flags
+ ///
+ /// This method creates a request with its FQDN flags set to the given
+ /// value and submits it to the server for processing. It then checks
+ /// the following:
+ /// 1. Did the server generate an ACK with the correct FQDN flags
+ /// 2. If the server should have generated an NCR, did it? and If
+ /// so was it correct?
+ ///
+ /// @param client_flags Mask of client FQDN flags which are true
+ /// @param response_flags Mask of expected FQDN flags in the response
+ void flagVsConfigScenario(const uint8_t client_flags,
+ const uint8_t response_flags) {
+ Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, client_flags,
+ "myhost.example.com.",
+ Option4ClientFqdn::FULL, true);
+
+ // Process the request.
+ Pkt4Ptr reply;
+ ASSERT_NO_THROW(reply = srv_->processRequest(req));
+
+ // Verify the response and flags.
+ checkResponse(reply, DHCPACK, 1234);
+ checkFqdnFlags(reply, response_flags);
+
+ // There should be an NCR only if response S flag is 1.
+ /// @todo This logic will need to change if forward and reverse
+ /// updates are ever controlled independently.
+ if ((response_flags & Option4ClientFqdn::FLAG_S) == 0) {
+ ASSERT_EQ(0, srv_->name_change_reqs_.size());
+ } else {
+ // Verify that there is one NameChangeRequest generated as expected.
+ ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+ reply->getYiaddr().toText(),
+ "myhost.example.com.",
+ "", // empty DHCID means don't check it
+ time(NULL) + subnet_->getValid(),
+ subnet_->getValid(), true);
+ }
+ }
+
+
NakedDhcpv4Srv* srv_;
};
@@ -348,21 +448,101 @@ TEST_F(NameDhcpv4SrvTest, dhcidComputeFromHWAddr) {
EXPECT_EQ(dhcid_ref, dhcid.toStr());
}
+// Tests the following scenario:
+// - Updates are enabled
+// - All overrides are off
+// - Client requests forward update (N = 0, S = 1)
+//
+// Server should perform the update:
+// - Reponse flags should N = 0, S = 1, O = 0
+// - Should queue an NCR
+TEST_F(NameDhcpv4SrvTest, updatesEnabled) {
+ flagVsConfigScenario((Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S),
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S));
+}
-// Test that server confirms to perform the forward and reverse DNS update,
-// when client asks for it.
-TEST_F(NameDhcpv4SrvTest, serverUpdateForwardFqdn) {
- Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
- Option4ClientFqdn::FLAG_E |
- Option4ClientFqdn::FLAG_S,
- "myhost.example.com.",
- Option4ClientFqdn::FULL,
- true);
+// Tests the following scenario
+// - Updates are disabled
+// - Client requests forward update (N = 0, S = 1)
+//
+// Server should NOT perform updates:
+// - Response flags should N = 1, S = 0, O = 1
+// - Should not queue any NCRs
+TEST_F(NameDhcpv4SrvTest, updatesDisabled) {
+ disableD2();
+ flagVsConfigScenario((Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S),
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N |
+ Option4ClientFqdn::FLAG_O));
+}
- testProcessFqdn(query,
- Option4ClientFqdn::FLAG_E | Option4ClientFqdn::FLAG_S,
- "myhost.example.com.");
+// Tests the following scenario:
+// - Updates are enabled
+// - All overrides are off.
+// - Client requests no updates (N = 1, S = 0)
+//
+// Server should NOT perform updates:
+// - Response flags should N = 1, S = 0, O = 0
+// - Should not queue any NCRs
+TEST_F(NameDhcpv4SrvTest, respectNoUpdate) {
+ flagVsConfigScenario((Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N),
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N));
+}
+// Tests the following scenario:
+// - Updates are enabled
+// - override-no-update is on
+// - Client requests no updates (N = 1, S = 0)
+//
+// Server should override "no update" request and perform updates:
+// - Response flags should be N = 0, S = 1, O = 1
+// - Should queue an NCR
+TEST_F(NameDhcpv4SrvTest, overrideNoUpdate) {
+ enableD2(OVERRIDE_NO_UPDATE);
+ flagVsConfigScenario((Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N),
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S |
+ Option4ClientFqdn::FLAG_O));
+}
+
+// Tests the following scenario:
+// - Updates are enabled
+// - All overrides are off.
+// - Client requests delegation (N = 0, S = 0)
+//
+// Server should respect client's delegation request and NOT do updates:
+
+// - Response flags should be N = 1, S = 0, O = 0
+// - Should not queue any NCRs
+TEST_F(NameDhcpv4SrvTest, respectClientDelegation) {
+
+ flagVsConfigScenario(Option4ClientFqdn::FLAG_E,
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N));
+}
+
+// Tests the following scenario:
+// - Updates are enabled
+// - override-client-update is on.
+// - Client requests delegation (N = 0, S = 0)
+//
+// Server should override client's delegation request and do updates:
+// - Response flags should be N = 0, S = 1, O = 1
+// - Should queue an NCR
+TEST_F(NameDhcpv4SrvTest, overrideClientDelegation) {
+ // Turn on override-client-update.
+ enableD2(OVERRIDE_CLIENT_UPDATE);
+
+ flagVsConfigScenario(Option4ClientFqdn::FLAG_E,
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S |
+ Option4ClientFqdn::FLAG_O));
}
// Test that server processes the Hostname option sent by a client and
@@ -447,34 +627,6 @@ TEST_F(NameDhcpv4SrvTest, serverUpdateForwardNoNameFqdn) {
}
-// Test server's response when client requests no DNS update.
-TEST_F(NameDhcpv4SrvTest, noUpdateFqdn) {
- Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
- Option4ClientFqdn::FLAG_E |
- Option4ClientFqdn::FLAG_N,
- "myhost.example.com.",
- Option4ClientFqdn::FULL,
- true);
- testProcessFqdn(query, Option4ClientFqdn::FLAG_E |
- Option4ClientFqdn::FLAG_N,
- "myhost.example.com.");
-}
-
-// Test that server does not accept delegation of the forward DNS update
-// to a client.
-TEST_F(NameDhcpv4SrvTest, clientUpdateNotAllowedFqdn) {
- Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
- Option4ClientFqdn::FLAG_E,
- "myhost.example.com.",
- Option4ClientFqdn::FULL,
- true);
-
- testProcessFqdn(query, Option4ClientFqdn::FLAG_E |
- Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_O,
- "myhost.example.com.");
-
-}
-
// Test that exactly one NameChangeRequest is generated when the new lease
// has been acquired (old lease is NULL).
TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsNewLease) {
@@ -600,18 +752,42 @@ TEST_F(NameDhcpv4SrvTest, processRequestFqdnEmptyDomainName) {
// Verify that there is one NameChangeRequest generated.
ASSERT_EQ(1, srv_->name_change_reqs_.size());
+
// The hostname is generated from the IP address acquired (yiaddr).
- std::ostringstream hostname;
- hostname << generatedNameFromAddress(reply->getYiaddr())
- << ".example.com.";
+ std::string hostname = generatedNameFromAddress(reply->getYiaddr());
+
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
- reply->getYiaddr().toText(), hostname.str(),
+ reply->getYiaddr().toText(), hostname,
"", // empty DHCID forces that it is not checked
time(NULL) + subnet_->getValid(),
subnet_->getValid(), true);
}
// Test that server generates client's hostname from the IP address assigned
+// to it when DHCPv4 Client FQDN option specifies an empty domain-name AND
+// ddns updates are disabled.
+TEST_F(NameDhcpv4SrvTest, processRequestEmptyDomainNameDisabled) {
+ disableD2();
+ Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+ Option4ClientFqdn::FLAG_E,
+ "", Option4ClientFqdn::PARTIAL, true);
+ Pkt4Ptr reply;
+ ASSERT_NO_THROW(reply = srv_->processRequest(req));
+
+ checkResponse(reply, DHCPACK, 1234);
+
+ Option4ClientFqdnPtr fqdn = getClientFqdnOption(reply);
+ ASSERT_TRUE(fqdn);
+
+ // The hostname is generated from the IP address acquired (yiaddr).
+ std::string hostname = generatedNameFromAddress(reply->getYiaddr());
+
+ EXPECT_EQ(hostname, fqdn->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::FULL, fqdn->getDomainNameType());
+}
+
+
+// Test that server generates client's hostname from the IP address assigned
// to it when Hostname option carries the top level domain-name.
TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
Pkt4Ptr req = generatePktWithHostname(DHCPREQUEST, ".");
@@ -626,11 +802,12 @@ TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
// Verify that there is one NameChangeRequest generated.
ASSERT_EQ(1, srv_->name_change_reqs_.size());
+
// The hostname is generated from the IP address acquired (yiaddr).
- std::ostringstream hostname;
- hostname << generatedNameFromAddress(reply->getYiaddr()) << ".example.com.";
+ std::string hostname = generatedNameFromAddress(reply->getYiaddr());
+
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
- reply->getYiaddr().toText(), hostname.str(),
+ reply->getYiaddr().toText(), hostname,
"", // empty DHCID forces that it is not checked
time(NULL), subnet_->getValid(), true);
}
@@ -742,21 +919,23 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
time(NULL), subnet_->getValid(), true);
}
-// Test that when the Release message is sent for the previously acquired
-// lease, then server genenerates a NameChangeRequest to remove the entries
-// corresponding to the lease being released.
+// Test that when a release message is sent for a previously acquired lease,
+// DDNS updates are enabled that the server genenerates a NameChangeRequest
+// to remove entries corresponding to the released lease.
TEST_F(NameDhcpv4SrvTest, processRequestRelease) {
+ // Verify the updates are enabled.
+ ASSERT_TRUE(CfgMgr::instance().ddnsEnabled());
+
+ // Create and process a lease request so we have a lease to release.
Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
"myhost.example.com.",
Option4ClientFqdn::FULL, true);
-
Pkt4Ptr reply;
ASSERT_NO_THROW(reply = srv_->processRequest(req));
-
checkResponse(reply, DHCPACK, 1234);
- // Verify that there is one NameChangeRequest generated.
+ // Verify that there is one NameChangeRequest generated for lease.
ASSERT_EQ(1, srv_->name_change_reqs_.size());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
reply->getYiaddr().toText(), "myhost.example.com.",
@@ -764,18 +943,57 @@ TEST_F(NameDhcpv4SrvTest, processRequestRelease) {
"965B68B6D438D98E680BF10B09F3BCF",
time(NULL), subnet_->getValid(), true);
- // Create a Release message.
+ // Create and process the Release message.
Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
rel->setCiaddr(reply->getYiaddr());
rel->setRemoteAddr(IOAddress("192.0.2.3"));
rel->addOption(generateClientId());
rel->addOption(srv_->getServerID());
-
ASSERT_NO_THROW(srv_->processRelease(rel));
// The lease has been removed, so there should be a NameChangeRequest to
// remove corresponding DNS entries.
ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
+ reply->getYiaddr().toText(), "myhost.example.com.",
+ "00010132E91AA355CFBB753C0F0497A5A940436"
+ "965B68B6D438D98E680BF10B09F3BCF",
+ time(NULL), subnet_->getValid(), true);
+}
+
+// Test that when the Release message is sent for a previously acquired lease
+// and DDNS updates are disabled that server does NOT generate a
+// NameChangeRequest to remove entries corresponding to the released lease.
+TEST_F(NameDhcpv4SrvTest, processRequestReleaseUpdatesDisabled) {
+ // Disable DDNS.
+ disableD2();
+ ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+ // Create and process a lease request so we have a lease to release.
+ Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+ Option4ClientFqdn::FLAG_E,
+ "myhost.example.com.",
+ Option4ClientFqdn::FULL, true);
+ Pkt4Ptr reply;
+ ASSERT_NO_THROW(reply = srv_->processRequest(req));
+ checkResponse(reply, DHCPACK, 1234);
+
+ // With DDNS updates disabled, there should be not be a NameChangeRequest
+ // for the add.
+ ASSERT_EQ(0, srv_->name_change_reqs_.size());
+
+ // Create and process the Release message.
+ Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
+ rel->setCiaddr(reply->getYiaddr());
+ rel->setRemoteAddr(IOAddress("192.0.2.3"));
+ rel->addOption(generateClientId());
+ rel->addOption(srv_->getServerID());
+ ASSERT_NO_THROW(srv_->processRelease(rel));
+
+ // With DDNS updates disabled, there should be not be a NameChangeRequest
+ // for the remove.
+ ASSERT_EQ(0, srv_->name_change_reqs_.size());
}
+
} // end of anonymous namespace
diff --git a/src/lib/dhcp/option4_client_fqdn.cc b/src/lib/dhcp/option4_client_fqdn.cc
index 7f93a50..d546ab2 100644
--- a/src/lib/dhcp/option4_client_fqdn.cc
+++ b/src/lib/dhcp/option4_client_fqdn.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -217,7 +217,6 @@ setDomainName(const std::string& domain_name,
} else {
try {
domain_name_.reset(new isc::dns::Name(name));
- domain_name_type_ = name_type;
} catch (const Exception& ex) {
isc_throw(InvalidOption4FqdnDomainName,
@@ -227,6 +226,8 @@ setDomainName(const std::string& domain_name,
}
}
+
+ domain_name_type_ = name_type;
}
void
diff --git a/src/lib/dhcp/option6_client_fqdn.cc b/src/lib/dhcp/option6_client_fqdn.cc
index 761acae..d9eb0b2 100644
--- a/src/lib/dhcp/option6_client_fqdn.cc
+++ b/src/lib/dhcp/option6_client_fqdn.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -188,7 +188,6 @@ setDomainName(const std::string& domain_name,
} else {
try {
domain_name_.reset(new isc::dns::Name(name, true));
- domain_name_type_ = name_type;
} catch (const Exception& ex) {
isc_throw(InvalidOption6FqdnDomainName, "invalid domain-name value '"
@@ -197,6 +196,8 @@ setDomainName(const std::string& domain_name,
}
}
+
+ domain_name_type_ = name_type;
}
void
diff --git a/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc b/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
index cdd7c64..ae658d2 100644
--- a/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
+++ b/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -615,6 +615,7 @@ TEST(Option4ClientFqdnTest, setDomainName) {
// Empty domain name (partial). This should be successful.
ASSERT_NO_THROW(option->setDomainName("", Option4ClientFqdn::PARTIAL));
EXPECT_TRUE(option->getDomainName().empty());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, option->getDomainNameType());
// Fully qualified domain-names must not be empty.
EXPECT_THROW(option->setDomainName("", Option4ClientFqdn::FULL),
diff --git a/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc b/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
index d644a2e..dac38e1 100644
--- a/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
+++ b/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -537,6 +537,7 @@ TEST(Option6ClientFqdnTest, setDomainName) {
// Empty domain name (partial). This should be successful.
ASSERT_NO_THROW(option->setDomainName("", Option6ClientFqdn::PARTIAL));
EXPECT_TRUE(option->getDomainName().empty());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, option->getDomainNameType());
// Fully qualified domain-names must not be empty.
EXPECT_THROW(option->setDomainName("", Option6ClientFqdn::FULL),
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index 6f008f1..8d41a15 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -363,6 +363,10 @@ CfgMgr::getD2ClientConfig() const {
return (d2_client_mgr_.getD2ClientConfig());
}
+D2ClientMgr&
+CfgMgr::getD2ClientMgr() {
+ return (d2_client_mgr_);
+}
CfgMgr::CfgMgr()
: datadir_(DHCP_DATA_DIR),
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index fcec8bf..18455d0 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -341,7 +341,7 @@ public:
/// pointer.
void setD2ClientConfig(D2ClientConfigPtr& new_config);
- /// @param Convenience method for checking if DHCP-DDNS updates are enabled.
+ /// @brief Convenience method for checking if DHCP-DDNS updates are enabled.
///
/// @return True if the D2 configuration is enabled.
bool ddnsEnabled();
@@ -351,6 +351,11 @@ public:
/// @return a reference to the current configuration pointer.
const D2ClientConfigPtr& getD2ClientConfig() const;
+ /// @brief Fetches the DHCP-DDNS manager.
+ ///
+ /// @return a reference to the DHCP-DDNS manager.
+ D2ClientMgr& getD2ClientMgr();
+
protected:
/// @brief Protected constructor.
diff --git a/src/lib/dhcpsrv/d2_client.cc b/src/lib/dhcpsrv/d2_client.cc
index d1b06ae..419c5ce 100644
--- a/src/lib/dhcpsrv/d2_client.cc
+++ b/src/lib/dhcpsrv/d2_client.cc
@@ -29,7 +29,6 @@ D2ClientConfig::D2ClientConfig(const bool enable_updates,
NameChangeProtocol& ncr_protocol,
const dhcp_ddns::
NameChangeFormat& ncr_format,
- const bool remove_on_renew,
const bool always_include_fqdn,
const bool override_no_update,
const bool override_client_update,
@@ -41,7 +40,6 @@ D2ClientConfig::D2ClientConfig(const bool enable_updates,
server_port_(server_port),
ncr_protocol_(ncr_protocol),
ncr_format_(ncr_format),
- remove_on_renew_(remove_on_renew),
always_include_fqdn_(always_include_fqdn),
override_no_update_(override_no_update),
override_client_update_(override_client_update),
@@ -57,13 +55,12 @@ D2ClientConfig::D2ClientConfig()
server_port_(0),
ncr_protocol_(dhcp_ddns::NCR_UDP),
ncr_format_(dhcp_ddns::FMT_JSON),
- remove_on_renew_(false),
always_include_fqdn_(false),
override_no_update_(false),
override_client_update_(false),
replace_client_name_(false),
- generated_prefix_(""),
- qualifying_suffix_("") {
+ generated_prefix_("myhost"),
+ qualifying_suffix_("example.com") {
validateContents();
}
@@ -83,9 +80,8 @@ D2ClientConfig::validateContents() {
<< " is not yet supported");
}
- // @todo perhaps more validation we should do yet?
- // Are there any invalid combinations of options we need to test against?
- // Also do we care about validating contents if it's disabled?
+ /// @todo perhaps more validation we should do yet?
+ /// Are there any invalid combinations of options we need to test against?
}
bool
@@ -95,7 +91,6 @@ D2ClientConfig::operator == (const D2ClientConfig& other) const {
(server_port_ == other.server_port_) &&
(ncr_protocol_ == other.ncr_protocol_) &&
(ncr_format_ == other.ncr_format_) &&
- (remove_on_renew_ == other.remove_on_renew_) &&
(always_include_fqdn_ == other.always_include_fqdn_) &&
(override_no_update_ == other.override_no_update_) &&
(override_client_update_ == other.override_client_update_) &&
@@ -119,7 +114,6 @@ D2ClientConfig::toText() const {
<< ", server_port: " << server_port_
<< ", ncr_protocol: " << ncr_protocol_
<< ", ncr_format: " << ncr_format_
- << ", remove_on_renew: " << (remove_on_renew_ ? "yes" : "no")
<< ", always_include_fqdn: " << (always_include_fqdn_ ?
"yes" : "no")
<< ", override_no_update: " << (override_no_update_ ?
@@ -142,7 +136,7 @@ operator<<(std::ostream& os, const D2ClientConfig& config) {
}
D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()) {
- // Default contstructor initializes with a disabled config.
+ // Default constructor initializes with a disabled configuration.
}
D2ClientMgr::~D2ClientMgr(){
@@ -159,8 +153,8 @@ D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
// scenarios:
// 1. D2 was enabled but now it is disabled
// - destroy the sender, flush any queued
- // 2. D2 is still enabled but server params have changed
- // - preserve any queued, reconnect based on sender params
+ // 2. D2 is still enabled but server parameters have changed
+ // - preserve any queued, reconnect based on sender parameters
// 3. D2 was was disabled now it is enabled.
// - create sender
//
@@ -181,5 +175,89 @@ D2ClientMgr::getD2ClientConfig() const {
return (d2_client_config_);
}
+void
+D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
+ bool& server_s, bool& server_n) const {
+ // Per RFC 4702 & 4704, the client N and S flags allow the client to
+ // request one of three options:
+ //
+ // N flag S flag Option
+ // ------------------------------------------------------------------
+ // 0 0 client wants to do forward updates (section 3.2)
+ // 0 1 client wants server to do forward updates (section 3.3)
+ // 1 0 client wants no one to do updates (section 3.4)
+ // 1 1 invalid combination
+ // (Note section numbers cited are for 4702, for 4704 see 5.1, 5.2, and 5.3)
+ //
+ // Make a bit mask from the client's flags and use it to set the response
+ // flags accordingly.
+ const uint8_t mask = ((client_n ? 2 : 0) + (client_s ? 1 : 0));
+
+ switch (mask) {
+ case 0:
+ // If updates are enabled and we are overriding client delegation
+ // then S flag should be true.
+ server_s = (d2_client_config_->getEnableUpdates() &&
+ d2_client_config_->getOverrideClientUpdate());
+ break;
+
+ case 1:
+ server_s = d2_client_config_->getEnableUpdates();
+ break;
+
+ case 2:
+ // If updates are enabled and we are overriding "no updates" then
+ // S flag should be true.
+ server_s = (d2_client_config_->getEnableUpdates() &&
+ d2_client_config_->getOverrideNoUpdate());
+ break;
+
+ default:
+ // RFCs declare this an invalid combination.
+ isc_throw(isc::BadValue,
+ "Invalid client FQDN - N and S cannot both be 1");
+ break;
+ }
+
+ /// @todo Currently we are operating under the premise that N should be 1
+ /// if the server is not doing updates nor do we have configuration
+ /// controls to govern forward and reverse updates independently.
+ /// In addition, the client FQDN flags cannot explicitly suggest what to
+ /// do with reverse updates. They request either forward updates or no
+ /// updates. In other words, the client cannot request the server do or
+ /// not do reverse updates. For now, we are either going to do updates in
+ /// both directions or none at all. If and when additional configuration
+ /// parameters are added this logic will have to be reassessed.
+ server_n = !server_s;
+}
+
+std::string
+D2ClientMgr::generateFqdn(const asiolink::IOAddress& address) const {
+ std::string hostname = address.toText();
+ std::replace(hostname.begin(), hostname.end(),
+ (address.isV4() ? '.' : ':'), '-');
+
+ std::ostringstream gen_name;
+ gen_name << d2_client_config_->getGeneratedPrefix() << "-" << hostname;
+ return (qualifyName(gen_name.str()));
+}
+
+std::string
+D2ClientMgr::qualifyName(const std::string& partial_name) const {
+ std::ostringstream gen_name;
+ gen_name << partial_name << "." << d2_client_config_->getQualifyingSuffix();
+
+ // Tack on a trailing dot in case suffix doesn't have one.
+ std::string str = gen_name.str();
+ size_t len = str.length();
+ if ((len > 0) && (str[len - 1] != '.')) {
+ gen_name << ".";
+ }
+
+ return (gen_name.str());
+}
+
+
+
}; // namespace dhcp
}; // namespace isc
diff --git a/src/lib/dhcpsrv/d2_client.h b/src/lib/dhcpsrv/d2_client.h
index 2f84515..0a6faa4 100644
--- a/src/lib/dhcpsrv/d2_client.h
+++ b/src/lib/dhcpsrv/d2_client.h
@@ -64,11 +64,6 @@ public:
/// Currently only UDP is supported.
/// @param ncr_format Format of the b10-dhcp-ddns requests.
/// Currently only JSON format is supported.
- /// @param remove_on_renew Enables DNS Removes when renewing a lease
- /// If true, Kea should request an explicit DNS remove prior to requesting
- /// a DNS update when renewing a lease.
- /// (Note: b10-dhcp-ddns is implemented per RFC 4703 and such a remove
- /// is unnecessary).
/// @param always_include_fqdn Enables always including the FQDN option in
/// DHCP responses.
/// @param override_no_update Enables updates, even if clients request no
@@ -86,7 +81,6 @@ public:
const size_t server_port,
const dhcp_ddns::NameChangeProtocol& ncr_protocol,
const dhcp_ddns::NameChangeFormat& ncr_format,
- const bool remove_on_renew,
const bool always_include_fqdn,
const bool override_no_update,
const bool override_client_update,
@@ -126,11 +120,6 @@ public:
return(ncr_format_);
}
- /// @brief Return whether or not removes should be sent for lease renewals.
- bool getRemoveOnRenew() const {
- return(remove_on_renew_);
- }
-
/// @brief Return whether or not FQDN is always included in DHCP responses.
bool getAlwaysIncludeFqdn() const {
return(always_include_fqdn_);
@@ -189,20 +178,13 @@ private:
size_t server_port_;
/// @brief The socket protocol to use with b10-dhcp-ddns.
- /// Currently only UPD is supported.
+ /// Currently only UDP is supported.
dhcp_ddns::NameChangeProtocol ncr_protocol_;
/// @brief Format of the b10-dhcp-ddns requests.
/// Currently only JSON format is supported.
dhcp_ddns::NameChangeFormat ncr_format_;
- /// @brief Should Kea request a DNS Remove when renewing a lease.
- /// If true, Kea should request an explicit DNS remove prior to requesting
- /// a DNS update when renewing a lease.
- /// (Note: b10-dhcp-ddns is implemented per RFC 4703 and such a remove
- /// is unnecessary).
- bool remove_on_renew_;
-
/// @brief Should Kea always include the FQDN option in its response.
bool always_include_fqdn_;
@@ -263,11 +245,133 @@ public:
/// @return a reference to the current configuration pointer.
const D2ClientConfigPtr& getD2ClientConfig() const;
+ /// @brief Determines server flags based on configuration and client flags.
+ ///
+ /// This method uses input values for the client's FQDN S and N flags, in
+ /// conjunction with the configuration parameters updates-enabled, override-
+ /// no-updates, and override-client-updates to determine the values that
+ /// should be used for the server's FQDN S and N flags.
+ /// The logic in this method is based upon RFCs 4702 and 4704.
+ ///
+ /// @param client_s S Flag from the client's FQDN
+ /// @param client_n N Flag from the client's FQDN
+ /// @param server_s [out] S Flag for the server's FQDN
+ /// @param server_n [out] N Flag for the server's FQDN
+ ///
+ /// @throw isc::BadValue if client_s and client_n are both 1 as this is
+ /// an invalid combination per RFCs.
+ void analyzeFqdn(const bool client_s, const bool client_n, bool& server_s,
+ bool& server_n) const;
+
+ /// @brief Builds a FQDN based on the configuration and given IP address.
+ ///
+ /// Using the current values for generated-prefix, qualifying-suffix and
+ /// an IP address, this method constructs a fully qualified domain name.
+ /// It supports both IPv4 and IPv6 addresses. The format of the name
+ /// is as follows:
+ ///
+ /// <generated-prefix>-<ip address>.<qualifying-suffix>.
+ ///
+ /// <ip-address> is the result of IOAddress.toText() with the delimiters
+ /// ('.' for IPv4 or ':' for IPv6) replaced with a hyphen, '-'.
+ ///
+ /// @param address IP address from which to derive the name (IPv4 or IPv6)
+ ///
+ /// @return std::string containing the generated name.
+ std::string generateFqdn(const asiolink::IOAddress& address) const;
+
+ /// @brief Adds a qualifying suffix to a given domain name
+ ///
+ /// Constructs a FQDN based on the configured qualifying-suffix and
+ /// a partial domain name as follows:
+ ///
+ /// <partial_name>.<qualifying-suffix>.
+ /// Note it will add a trailing '.' should qualifying-suffix not end with
+ /// one.
+ ///
+ /// @param partial_name domain name to qualify
+ ///
+ /// @return std::string containing the qualified name.
+ std::string qualifyName(const std::string& partial_name) const;
+
+ /// @brief Set server FQDN flags based on configuration and a given FQDN
+ ///
+ /// Templated wrapper around the analyzeFqdn() allowing that method to
+ /// be used for either IPv4 or IPv6 processing. This methods resets all
+ /// of the flags in the response to zero and then sets the S,N, and O
+ /// flags. Any other flags are the responsiblity of the invoking layer.
+ ///
+ /// @param fqdn FQDN option from which to read client (inbound) flags
+ /// @param fqdn_resp FQDN option to update with the server (outbound) flags
+ /// @tparam T FQDN Option class containing the FQDN data such as
+ /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
+ template <class T>
+ void adjustFqdnFlags(const T& fqdn, T& fqdn_resp);
+
+ /// @brief Set server FQDN name based on configuration and a given FQDN
+ ///
+ /// Templated method which adjusts the domain name value and type in
+ /// a server FQDN from a client (inbound) FQDN and the current
+ /// configuration. The logic is as follows:
+ ///
+ /// If replace-client-name is true or the supplied name is empty, the
+ /// server FQDN is set to ""/PARTIAL.
+ ///
+ /// If replace-client-name is false and the supplied name is a partial
+ /// name the server FQDN is set to the supplied name qualified by
+ /// appending the qualifying-suffix.
+ ///
+ /// If replace-client-name is false and the supplied name is a fully
+ /// qualified name, set the server FQDN to the supplied name.
+ ///
+ /// @param fqdn FQDN option from which to get client (inbound) name
+ /// @param fqdn_resp FQDN option to update with the adjusted name
+ /// @tparam T FQDN Option class containing the FQDN data such as
+ /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
+ template <class T>
+ void adjustDomainName(const T& fqdn, T& fqdn_resp);
+
private:
/// @brief Container class for DHCP-DDNS configuration parameters.
D2ClientConfigPtr d2_client_config_;
};
+template <class T>
+void
+D2ClientMgr::adjustFqdnFlags(const T& fqdn, T& fqdn_resp) {
+ bool server_s = false;
+ bool server_n = false;
+ analyzeFqdn(fqdn.getFlag(T::FLAG_S), fqdn.getFlag(T::FLAG_N),
+ server_s, server_n);
+
+ // Reset the flags to zero to avoid triggering N and S both 1 check.
+ fqdn_resp.resetFlags();
+
+ // Set S and N flags.
+ fqdn_resp.setFlag(T::FLAG_S, server_s);
+ fqdn_resp.setFlag(T::FLAG_N, server_n);
+
+ // Set O flag true if server S overrides client S.
+ fqdn_resp.setFlag(T::FLAG_O, (fqdn.getFlag(T::FLAG_S) != server_s));
+}
+
+
+template <class T>
+void
+D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp) {
+ // If we're configured to replace it or the supplied name is blank
+ // set the response name to blank.
+ if (d2_client_config_->getReplaceClientName() ||
+ fqdn.getDomainName().empty()) {
+ fqdn_resp.setDomainName("", T::PARTIAL);
+ } else {
+ // If the supplied name is partial, qualify it by adding the suffix.
+ if (fqdn.getDomainNameType() == T::PARTIAL) {
+ fqdn_resp.setDomainName(qualifyName(fqdn.getDomainName()), T::FULL);
+ }
+ }
+}
+
/// @brief Defines a pointer for D2ClientMgr instances.
typedef boost::shared_ptr<D2ClientMgr> D2ClientMgrPtr;
diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc
index ab651a3..7aea310 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/dhcp_parsers.cc
@@ -1200,10 +1200,11 @@ D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
}
bool enable_updates = boolean_values_->getParam("enable-updates");
- if (!enable_updates) {
- // If it's not enabled, don't bother validating the rest. This
- // allows for an abbreviated config entry that only contains
- // the flag. The default constructor creates a disabled instance.
+ if (!enable_updates && (client_config->mapValue().size() == 1)) {
+ // If enable-updates is the only parameter and it is false then
+ // we're done. This allows for an abbreviated configuration entry
+ // that only contains that flag. Use the default D2ClientConfig
+ // constructor to a create a disabled instance.
local_client_config_.reset(new D2ClientConfig());
return;
}
@@ -1225,7 +1226,6 @@ D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
std::string qualifying_suffix = string_values_->
getParam("qualifying-suffix");
- bool remove_on_renew = boolean_values_->getParam("remove-on-renew");
bool always_include_fqdn = boolean_values_->getParam("always-include-fqdn");
bool override_no_update = boolean_values_->getParam("override-no-update");
bool override_client_update = boolean_values_->
@@ -1235,7 +1235,7 @@ D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
// Attempt to create the new client config.
local_client_config_.reset(new D2ClientConfig(enable_updates, server_ip,
server_port, ncr_protocol,
- ncr_format, remove_on_renew,
+ ncr_format,
always_include_fqdn,
override_no_update,
override_client_update,
@@ -1256,7 +1256,6 @@ D2ClientConfigParser::createConfigParser(const std::string& config_id) {
(config_id.compare("qualifying-suffix") == 0)) {
parser = new StringParser(config_id, string_values_);
} else if ((config_id.compare("enable-updates") == 0) ||
- (config_id.compare("remove-on-renew") == 0) ||
(config_id.compare("always-include-fqdn") == 0) ||
(config_id.compare("allow-client-update") == 0) ||
(config_id.compare("override-no-update") == 0) ||
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index 8c3d2ea..e13cb32 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -672,15 +672,17 @@ TEST_F(CfgMgrTest, echoClientId) {
// This test checks the D2ClientMgr wrapper methods.
TEST_F(CfgMgrTest, d2ClientConfig) {
- // After CfgMgr construction, D2 configuration should be disabled.
- // Fetch it and verify this is the case.
+ // After CfgMgr construction, D2ClientMgr member should be initialized
+ // with a D2 configuration that is disabled.
+ // Verify we can Fetch the mgr.
+ D2ClientMgr d2_mgr = CfgMgr::instance().getD2ClientMgr();
+ EXPECT_FALSE(d2_mgr.ddnsEnabled());
+
+ // Make sure the convenience method fetches the config correctly.
D2ClientConfigPtr original_config = CfgMgr::instance().getD2ClientConfig();
ASSERT_TRUE(original_config);
EXPECT_FALSE(original_config->getEnableUpdates());
- // Make sure convenience method agrees.
- EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
-
// Verify that we cannot set the configuration to an empty pointer.
D2ClientConfigPtr new_cfg;
ASSERT_THROW(CfgMgr::instance().setD2ClientConfig(new_cfg), D2ClientError);
@@ -689,7 +691,7 @@ TEST_F(CfgMgrTest, d2ClientConfig) {
ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true,
isc::asiolink::IOAddress("127.0.0.1"), 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
// Verify that we can assign a new, non-empty configuration.
diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc
index bd8f061..8a8550f 100644
--- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc
+++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc
@@ -13,6 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
+#include <dhcp/option4_client_fqdn.h>
+#include <dhcp/option6_client_fqdn.h>
#include <dhcpsrv/d2_client.h>
#include <exceptions/exceptions.h>
@@ -41,7 +43,6 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
size_t server_port = 477;
dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP;
dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON;
- bool remove_on_renew = true;
bool always_include_fqdn = true;
bool override_no_update = true;
bool override_client_update = true;
@@ -56,7 +57,6 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
server_port,
ncr_protocol,
ncr_format,
- remove_on_renew,
always_include_fqdn,
override_no_update,
override_client_update,
@@ -73,7 +73,6 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
EXPECT_EQ(d2_client_config->getServerPort(), server_port);
EXPECT_EQ(d2_client_config->getNcrProtocol(), ncr_protocol);
EXPECT_EQ(d2_client_config->getNcrFormat(), ncr_format);
- EXPECT_EQ(d2_client_config->getRemoveOnRenew(), remove_on_renew);
EXPECT_EQ(d2_client_config->getAlwaysIncludeFqdn(), always_include_fqdn);
EXPECT_EQ(d2_client_config->getOverrideNoUpdate(), override_no_update);
EXPECT_EQ(d2_client_config->getOverrideClientUpdate(),
@@ -94,7 +93,6 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
server_port,
dhcp_ddns::NCR_TCP,
ncr_format,
- remove_on_renew,
always_include_fqdn,
override_no_update,
override_client_update,
@@ -119,7 +117,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(ref_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(ref_config);
@@ -127,7 +125,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_TRUE(*ref_config == *test_config);
@@ -137,7 +135,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(false,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -147,7 +145,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
test_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -157,17 +155,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 333,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
- "pre-fix", "suf-fix")));
- ASSERT_TRUE(test_config);
- EXPECT_FALSE(*ref_config == *test_config);
- EXPECT_TRUE(*ref_config != *test_config);
-
- // Check a configuration that differs only by remove_on_renew.
- ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
- ref_address, 477,
- dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- false, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -177,7 +165,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, false, true, true, true,
+ false, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -187,7 +175,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, false, true, true,
+ true, false, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -197,7 +185,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, false, true,
+ true, true, false, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -207,7 +195,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, false,
+ true, true, true, false,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -217,7 +205,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"bogus", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -227,7 +215,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "bogus")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -270,7 +258,7 @@ TEST(D2ClientMgr, validConfig) {
ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true,
isc::asiolink::IOAddress("127.0.0.1"), 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
// Verify that we can assign a new, non-empty configuration.
@@ -291,4 +279,520 @@ TEST(D2ClientMgr, validConfig) {
}
+/// @brief Tests that analyzeFqdn detects invalid combination of both the
+/// client S and N flags set to true.
+TEST(D2ClientMgr, analyzeFqdnInvalidCombination) {
+ D2ClientMgr mgr;
+ bool server_s = false;
+ bool server_n = false;
+
+ // Create disabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig()));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_FALSE(mgr.ddnsEnabled());
+
+ // client S=1 N=1 is invalid. analyzeFqdn should throw.
+ ASSERT_THROW(mgr.analyzeFqdn(true, true, server_s, server_n),
+ isc::BadValue);
+
+ // Create enabled configuration with all controls off (no overrides).
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+
+ // client S=1 N=1 is invalid. analyzeFqdn should throw.
+ ASSERT_THROW(mgr.analyzeFqdn(true, true, server_s, server_n),
+ isc::BadValue);
+}
+
+/// @brief Tests that analyzeFqdn generates correct server S and N flags when
+/// updates are enabled and all overrides are off.
+TEST(D2ClientMgr, analyzeFqdnEnabledNoOverrides) {
+ D2ClientMgr mgr;
+ bool server_s = false;
+ bool server_n = false;
+
+ // Create enabled configuration with all controls off (no overrides).
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_FALSE(cfg->getOverrideClientUpdate());
+ ASSERT_FALSE(cfg->getOverrideNoUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 0 (server is not doing forward updates)
+ // and server N should be 1 (server doing no updates)
+ mgr.analyzeFqdn(false, false, server_s, server_n);
+ EXPECT_FALSE(server_s);
+ EXPECT_TRUE(server_n);
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ mgr.analyzeFqdn(true, false, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+
+
+ // client S=0 N=1 means client wants no one to do forward updates.
+ // server S should be 0 (server is not forward updates)
+ // and server N should be 1 (server is not doing any updates)
+ mgr.analyzeFqdn(false, true, server_s, server_n);
+ EXPECT_FALSE(server_s);
+ EXPECT_TRUE(server_n);
+}
+
+/// @brief Tests that analyzeFqdn generates correct server S and N flags when
+/// updates are enabled and override-no-update is on.
+TEST(D2ClientMgr, analyzeFqdnEnabledOverrideNoUpdate) {
+ D2ClientMgr mgr;
+ bool server_s = false;
+ bool server_n = false;
+
+ // Create enabled configuration with OVERRIDE_NO_UPDATE on.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, true, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_TRUE(cfg->getOverrideNoUpdate());
+ ASSERT_FALSE(cfg->getOverrideClientUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 0 (server is not doing forward updates)
+ // and server N should be 1 (server is not doing any updates)
+ mgr.analyzeFqdn(false, false, server_s, server_n);
+ EXPECT_FALSE(server_s);
+ EXPECT_TRUE(server_n);
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ mgr.analyzeFqdn(true, false, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+
+ // client S=0 N=1 means client wants no one to do forward updates.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server is doing updates)
+ mgr.analyzeFqdn(false, true, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+}
+
+/// @brief Tests that analyzeFqdn generates correct server S and N flags when
+/// updates are enabled and override-client-update is on.
+TEST(D2ClientMgr, analyzeFqdnEnabledOverrideClientUpdate) {
+ D2ClientMgr mgr;
+ bool server_s = false;
+ bool server_n = false;
+
+ // Create enabled configuration with OVERRIDE_CLIENT_UPDATE on.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, true, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_FALSE(cfg->getOverrideNoUpdate());
+ ASSERT_TRUE(cfg->getOverrideClientUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ mgr.analyzeFqdn(false, false, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ mgr.analyzeFqdn(true, false, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+
+ // client S=0 N=1 means client wants no one to do forward updates.
+ // server S should be 0 (server is not forward updates)
+ // and server N should be 1 (server is not doing any updates)
+ mgr.analyzeFqdn(false, true, server_s, server_n);
+ EXPECT_FALSE(server_s);
+ EXPECT_TRUE(server_n);
+}
+
+/// @brief Verifies the adustFqdnFlags template with Option4ClientFqdn objects.
+/// Ensures that the method can set the N, S, and O flags properly.
+/// Other permutations are covered by analyzeFqdnFlag tests.
+TEST(D2ClientMgr, adjustFqdnFlagsV4) {
+ D2ClientMgr mgr;
+ Option4ClientFqdnPtr request;
+ Option4ClientFqdnPtr response;
+
+ // Create enabled configuration and override-no-update on.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, true, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_TRUE(cfg->getOverrideNoUpdate());
+ ASSERT_FALSE(cfg->getOverrideClientUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 0 (server is not doing forward updates)
+ // and server N should be 1 (server doing no updates)
+ // and server O should be 0
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_S));
+ EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_N));
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_O));
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ // and server O should be 0
+ request.reset(new Option4ClientFqdn(Option4ClientFqdn::FLAG_S,
+ Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+ EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_S));
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_N));
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_O));
+
+ // client S=0 N=1 means client wants no one to do updates
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ // and O should be 1 (overriding client S)
+ request.reset(new Option4ClientFqdn(Option4ClientFqdn::FLAG_N,
+ Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+ EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_S));
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_N));
+ EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_O));
+}
+
+/// @brief Tests the qualifyName method's ability to construct FQDNs
+TEST(D2ClientMgr, qualifyName) {
+ D2ClientMgr mgr;
+
+ // Create enabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, true, false,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+
+ // Verify that the qualifying suffix gets appended with trailing dot added.
+ std::string partial_name = "somehost";
+ std::string qualified_name = mgr.qualifyName(partial_name);
+ EXPECT_EQ("somehost.suffix.com.", qualified_name);
+
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, true, false,
+ "prefix", "hasdot.com.")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+
+ // Verify that the qualifying suffix gets appended without dot added.
+ qualified_name = mgr.qualifyName(partial_name);
+ EXPECT_EQ("somehost.hasdot.com.", qualified_name);
+}
+
+
+/// @brief Tests the generateFdqn method's ability to construct FQDNs
+TEST(D2ClientMgr, generateFqdn) {
+ D2ClientMgr mgr;
+
+ // Create enabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, true, false,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+
+ // Verify that it works with an IPv4 address.
+ asiolink::IOAddress v4address("192.0.2.75");
+ EXPECT_EQ("prefix-192-0-2-75.suffix.com.", mgr.generateFqdn(v4address));
+
+ // Verify that it works with an IPv6 address.
+ asiolink::IOAddress v6address("2001:db8::2");
+ EXPECT_EQ("prefix-2001-db8--2.suffix.com.", mgr.generateFqdn(v6address));
+
+ // Create a disabled config.
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig()));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+
+ // Verify names generate properly with a disabled configuration.
+ EXPECT_EQ("myhost-192-0-2-75.example.com.", mgr.generateFqdn(v4address));
+ EXPECT_EQ("myhost-2001-db8--2.example.com.", mgr.generateFqdn(v6address));
+}
+
+/// @brief Tests adjustDomainName template method with Option4ClientFqdn
+TEST(D2ClientMgr, adjustDomainNameV4) {
+ D2ClientMgr mgr;
+ Option4ClientFqdnPtr request;
+ Option4ClientFqdnPtr response;
+
+ // Create enabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, false,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_FALSE(cfg->getReplaceClientName());
+
+ // replace-client-name is false, client passes in empty fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+
+
+ // replace-client-name is false, client passes in a partial fqdn
+ // response should contain client's name plus the qualifying suffix.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("myhost.suffix.com.", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::FULL, response->getDomainNameType());
+
+
+ // replace-client-name is false, client passes in a full fqdn
+ // response domain should not be altered.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost.example.com.",
+ Option4ClientFqdn::FULL));
+ response.reset(new Option4ClientFqdn(*request));
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("myhost.example.com.", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::FULL, response->getDomainNameType());
+
+ // Create enabled configuration.
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, true,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(cfg->getReplaceClientName());
+
+ // replace-client-name is true, client passes in empty fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+
+ // replace-client-name is true, client passes in a partial fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+
+
+ // replace-client-name is true, client passes in a full fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost.example.com.",
+ Option4ClientFqdn::FULL));
+ response.reset(new Option4ClientFqdn(*request));
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+}
+
+/// @brief Tests adjustDomainName template method with Option6ClientFqdn
+TEST(D2ClientMgr, adjustDomainNameV6) {
+ D2ClientMgr mgr;
+ Option6ClientFqdnPtr request;
+ Option6ClientFqdnPtr response;
+
+ // Create enabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, false,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_FALSE(cfg->getReplaceClientName());
+
+ // replace-client-name is false, client passes in empty fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option6ClientFqdn(0, "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+
+ // replace-client-name is false, client passes in a partial fqdn
+ // response should contain client's name plus the qualifying suffix.
+ request.reset(new Option6ClientFqdn(0, "myhost",
+ Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("myhost.suffix.com.", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::FULL, response->getDomainNameType());
+
+
+ // replace-client-name is false, client passes in a full fqdn
+ // response domain should not be altered.
+ request.reset(new Option6ClientFqdn(0, "myhost.example.com.",
+ Option6ClientFqdn::FULL));
+ response.reset(new Option6ClientFqdn(*request));
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("myhost.example.com.", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::FULL, response->getDomainNameType());
+
+ // Create enabled configuration.
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, true,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(cfg->getReplaceClientName());
+
+ // replace-client-name is true, client passes in empty fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option6ClientFqdn(0, "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+
+ // replace-client-name is true, client passes in a partial fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option6ClientFqdn(0, "myhost",
+ Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+
+
+ // replace-client-name is true, client passes in a full fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option6ClientFqdn(0, "myhost.example.com.",
+ Option6ClientFqdn::FULL));
+ response.reset(new Option6ClientFqdn(*request));
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+}
+
+/// @brief Verifies the adustFqdnFlags template with Option6ClientFqdn objects.
+/// Ensures that the method can set the N, S, and O flags properly.
+/// Other permutations are covered by analyzeFqdnFlags tests.
+TEST(D2ClientMgr, adjustFqdnFlagsV6) {
+ D2ClientMgr mgr;
+ Option6ClientFqdnPtr request;
+ Option6ClientFqdnPtr response;
+
+ // Create enabled configuration and override-no-update on.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, true, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_TRUE(cfg->getOverrideNoUpdate());
+ ASSERT_FALSE(cfg->getOverrideClientUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 0 (server is not doing forward updates)
+ // and server N should be 1 (server doing no updates)
+ // and server O should be 0
+ request.reset(new Option6ClientFqdn(0, "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_S));
+ EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_N));
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_O));
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ // and server O should be 0
+ request.reset(new Option6ClientFqdn(Option6ClientFqdn::FLAG_S,
+ "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+ EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_S));
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_N));
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_O));
+
+ // client S=0 N=1 means client wants no one to do updates
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ // and O should be 1 (overriding client S)
+ request.reset(new Option6ClientFqdn(Option6ClientFqdn::FLAG_N,
+ "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+ EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_S));
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_N));
+ EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_O));
+}
+
} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
index 2e6adb9..78e3442 100644
--- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
@@ -720,7 +720,6 @@ TEST_F(ParseConfigTest, validD2Config) {
" \"server-port\" : 3432, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -746,7 +745,6 @@ TEST_F(ParseConfigTest, validD2Config) {
EXPECT_EQ(3432, d2_client_config->getServerPort());
EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
- EXPECT_TRUE(d2_client_config->getRemoveOnRenew());
EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
@@ -755,17 +753,16 @@ TEST_F(ParseConfigTest, validD2Config) {
EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
// Another valid Configuration string.
- // This one has IPV6 server ip, control flags false,
+ // This one is disabled, has IPV6 server ip, control flags false,
// empty prefix/suffix
std::string config_str2 =
"{ \"dhcp-ddns\" :"
" {"
- " \"enable-updates\" : true, "
+ " \"enable-updates\" : false, "
" \"server-ip\" : \"2001:db8::\", "
" \"server-port\" : 43567, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : false, "
" \"always-include-fqdn\" : false, "
" \"override-no-update\" : false, "
" \"override-client-update\" : false, "
@@ -779,18 +776,17 @@ TEST_F(ParseConfigTest, validD2Config) {
rcode = parseConfiguration(config_str2);
ASSERT_TRUE(rcode == 0) << error_text_;
- // Verify that DHCP-DDNS is enabled and we can fetch the configuration.
- EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
+ // Verify that DHCP-DDNS is disabled and we can fetch the configuration.
+ EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
ASSERT_TRUE(d2_client_config);
// Verify that the configuration values are as expected.
- EXPECT_TRUE(d2_client_config->getEnableUpdates());
+ EXPECT_FALSE(d2_client_config->getEnableUpdates());
EXPECT_EQ("2001:db8::", d2_client_config->getServerIp().toText());
EXPECT_EQ(43567, d2_client_config->getServerPort());
EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
- EXPECT_FALSE(d2_client_config->getRemoveOnRenew());
EXPECT_FALSE(d2_client_config->getAlwaysIncludeFqdn());
EXPECT_FALSE(d2_client_config->getOverrideNoUpdate());
EXPECT_FALSE(d2_client_config->getOverrideClientUpdate());
@@ -842,7 +838,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -859,7 +854,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -876,7 +870,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"Bogus\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -893,7 +886,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"TCP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -910,7 +902,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"Bogus\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -927,7 +918,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
// " \"server-port\" : 53001, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
More information about the bind10-changes
mailing list