BIND 10 trac3036, updated. 8c1e879aeb6043f2a26ba83a07b5a3ee87ae1855 [3036] Added implementation for the FQDN option processing.
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Jul 22 11:13:45 UTC 2013
The branch, trac3036 has been updated
via 8c1e879aeb6043f2a26ba83a07b5a3ee87ae1855 (commit)
from d803eeaf3f71b0f68d26fde29802adf2a55fa31d (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 8c1e879aeb6043f2a26ba83a07b5a3ee87ae1855
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Jul 22 13:13:22 2013 +0200
[3036] Added implementation for the FQDN option processing.
-----------------------------------------------------------------------
Summary of changes:
src/bin/dhcp6/dhcp6_srv.cc | 131 ++++++++++++++++++
src/bin/dhcp6/dhcp6_srv.h | 21 +++
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 142 ++++++++++++++++++++
src/lib/dhcp/option6_client_fqdn.cc | 10 +-
src/lib/dhcp/option6_client_fqdn.h | 3 +
src/lib/dhcp/tests/option6_client_fqdn_unittest.cc | 25 ++++
6 files changed, 331 insertions(+), 1 deletion(-)
-----------------------------------------------------------------------
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index ef69a94..3c61252 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -20,6 +20,7 @@
#include <dhcp/iface_mgr.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option6_addrlst.h>
+#include <dhcp/option6_client_fqdn.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_iaaddr.h>
@@ -56,6 +57,34 @@ using namespace std;
namespace isc {
namespace dhcp {
+namespace {
+
+// The following constants describe server's behavior with respect to the
+// DHCPv6 Client FQDN Option sent by a client. They will be removed
+// when DDNS parameters for DHCPv6 are implemented with the ticket #3034.
+
+// Should server always include the FQDN option in its response, regardless
+// if it has been requested in ORO (Disabled).
+const bool FQDN_ALWAYS_INCLUDE = false;
+// Enable AAAA RR update delegation to the client (Disabled).
+const bool FQDN_ALLOW_CLIENT_UPDATE = false;
+// Globally enable updates (Enabled).
+const bool FQDN_ENABLE_UPDATE = true;
+// The partial name generated for the client if empty name has been
+// supplied.
+const char* FQDN_GENERATED_PARTIAL_NAME = "myhost";
+// 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;
+
+}
+
/// @brief file name of a server-id file
///
/// Server must store its duid in persistent storage that must not change
@@ -631,6 +660,99 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
}
}
+void
+Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+ // Get Client FQDN Option from the client's message. If this option hasn't
+ // been included, do nothing.
+ Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
+ Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
+ if (!fqdn) {
+ return;
+ }
+
+ // Prepare the FQDN option which will be included in the response to
+ // the client.
+ Option6ClientFqdnPtr fqdn_resp(new Option6ClientFqdn(*fqdn));
+ // RFC 4704, section 6. - all flags set to 0.
+ fqdn_resp->resetFlags();
+
+ // 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 AAAA update is delegated to the client but
+ // server neither respects delegation of updates nor it is configured
+ // to send update on its own when client requested delegation.
+ if (!FQDN_ENABLE_UPDATE ||
+ (fqdn->getFlag(Option6ClientFqdn::FLAG_N) && !FQDN_OVERRIDE_NO_UPDATE) ||
+ (!fqdn->getFlag(Option6ClientFqdn::FLAG_S) && !FQDN_ALLOW_CLIENT_UPDATE &&
+ !FQDN_OVERRIDE_CLIENT_UPDATE)) {
+ fqdn_resp->setFlag(Option6ClientFqdn::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 AAAA update to the client but
+ // server doesn't respect delegation and it is configured to perform
+ // an update on its own when client requested delegation.
+ } else if (fqdn->getFlag(Option6ClientFqdn::FLAG_S) ||
+ (!fqdn->getFlag(Option6ClientFqdn::FLAG_S) &&
+ !FQDN_ALLOW_CLIENT_UPDATE && FQDN_OVERRIDE_CLIENT_UPDATE)) {
+ fqdn_resp->setFlag(Option6ClientFqdn::FLAG_S, true);
+ }
+
+ // Server MUST set the O flag if it has overridden the client's setting
+ // of S flag.
+ if (fqdn->getFlag(Option6ClientFqdn::FLAG_S) !=
+ fqdn_resp->getFlag(Option6ClientFqdn::FLAG_S)) {
+ fqdn_resp->setFlag(Option6ClientFqdn::FLAG_O, true);
+ }
+
+ // If client supplied partial or empty domain-name, server should
+ // generate one.
+ if (fqdn->getDomainNameType() == Option6ClientFqdn::PARTIAL) {
+ std::ostringstream name;
+ if (fqdn->getDomainName().empty()) {
+ name << FQDN_GENERATED_PARTIAL_NAME;
+ } else {
+ name << fqdn->getDomainName();
+ }
+ name << "." << FQDN_PARTIAL_SUFFIX;
+ fqdn_resp->setDomainName(name.str(), Option6ClientFqdn::FULL);
+
+ // Server may be configured to replace a name supplied by a client,
+ // even if client supplied fully qualified domain-name.
+ } else if (FQDN_REPLACE_CLIENT_NAME) {
+ std::ostringstream name;
+ name << FQDN_GENERATED_PARTIAL_NAME << "." << FQDN_PARTIAL_SUFFIX;
+ fqdn_resp->setDomainName(name.str(), Option6ClientFqdn::FULL);
+
+ }
+
+ // Server sends back the FQDN option to the client if client has requested
+ // it using Option Request Option. However, server may be configured to
+ // send the FQDN option in its response, regardless whether client requested
+ // it or not.
+ bool include_fqdn = FQDN_ALWAYS_INCLUDE;
+ if (!include_fqdn) {
+ OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast<
+ OptionUint16Array>(question->getOption(D6O_ORO));
+ if (oro) {
+ const std::vector<uint16_t>& values = oro->getValues();
+ for (int i = 0; i < values.size(); ++i) {
+ if (values[i] == D6O_CLIENT_FQDN) {
+ include_fqdn = true;
+ }
+ }
+ }
+ }
+
+ if (include_fqdn) {
+ answer->addOption(fqdn_resp);
+ }
+}
+
OptionPtr
Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
@@ -1025,6 +1147,8 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
assignLeases(solicit, advertise);
+ processClientFqdn(solicit, advertise);
+
return (advertise);
}
@@ -1041,6 +1165,8 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
assignLeases(request, reply);
+ processClientFqdn(request, reply);
+
return (reply);
}
@@ -1055,6 +1181,8 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
appendDefaultOptions(renew, reply);
appendRequestedOptions(renew, reply);
+ processClientFqdn(renew, reply);
+
renewLeases(renew, reply);
return reply;
@@ -1086,6 +1214,9 @@ Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
releaseLeases(release, reply);
+ // @todo If client sent a release and we should remove outstanding
+ // DNS records.
+
return reply;
}
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index c7b1f0f..a7f722c 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -264,6 +264,27 @@ protected:
/// @param answer server's message (IA_NA options will be added here)
void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer);
+ /// @brief Processes Client FQDN Option.
+ ///
+ /// This function retrieves DHCPv6 Client FQDN %Option (if any) from the
+ /// packet sent by a client and takes necessary actions upon this option.
+ /// Received option comprises flags field which controls what DNS updates
+ /// server should do. Server may override client's preference based on
+ /// the current configuration. Server indicates that it has overridden
+ /// the preference by storing DHCPv6 Client Fqdn %Option with the
+ /// appropriate flags in the response to a client. This option is also
+ /// used to communicate the client's domain-name which should be sent
+ /// to the DNS in the update. Again, server may act upon the received
+ /// domain-name, i.e. if the provided domain-name is partial it should
+ /// generate the fully qualified domain-name.
+ ///
+ /// All the logic required to form appropriate answer to the client is
+ /// held in this function.
+ ///
+ /// @param question Client's message.
+ /// @param answer Server's response to the client.
+ void processClientFqdn(const Pkt6Ptr& question, Pkt6Ptr& answer);
+
/// @brief Attempts to renew received addresses
///
/// It iterates through received IA_NA options and attempts to renew
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 328a3c5..2e305d9 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -21,6 +21,7 @@
#include <dhcp/option.h>
#include <dhcp/option_custom.h>
#include <dhcp/option6_addrlst.h>
+#include <dhcp/option6_client_fqdn.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option_int_array.h>
@@ -33,6 +34,7 @@
#include <util/buffer.h>
#include <util/range_utilities.h>
+#include <boost/pointer_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <unistd.h>
@@ -52,6 +54,10 @@ using namespace std;
// Maybe it should be isc::test?
namespace {
+const uint8_t FQDN_FLAG_S = 0x1;
+const uint8_t FQDN_FLAG_O = 0x2;
+const uint8_t FQDN_FLAG_N = 0x4;
+
class NakedDhcpv6Srv: public Dhcpv6Srv {
// "naked" Interface Manager, exposes internal members
public:
@@ -70,6 +76,7 @@ public:
using Dhcpv6Srv::processRequest;
using Dhcpv6Srv::processRenew;
using Dhcpv6Srv::processRelease;
+ using Dhcpv6Srv::processClientFqdn;
using Dhcpv6Srv::createStatusCode;
using Dhcpv6Srv::selectSubnet;
using Dhcpv6Srv::sanityCheck;
@@ -225,6 +232,8 @@ public:
EXPECT_EQ(expected_transid, rsp->getTransid());
}
+ // Generates client's packet holding an FQDN option.
+
virtual ~NakedDhcpv6SrvTest() {
// Let's clean up if there is such a file.
unlink(DUID_FILE);
@@ -329,6 +338,95 @@ public:
Pool6Ptr pool_;
};
+class FqdnDhcpv6SrvTest : public NakedDhcpv6SrvTest {
+public:
+ FqdnDhcpv6SrvTest() {
+ }
+
+ virtual ~FqdnDhcpv6SrvTest() {
+ }
+
+ Pkt6Ptr generatePktWithFqdn(uint8_t msg_type,
+ const uint8_t fqdn_flags,
+ const std::string& fqdn_domain_name,
+ const Option6ClientFqdn::DomainNameType
+ fqdn_type,
+ const bool include_oro,
+ OptionPtr srvid = OptionPtr()) {
+ Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
+ pkt->setRemoteAddr(IOAddress("fe80::abcd"));
+ pkt->addOption(generateIA(234, 1500, 3000));
+ OptionPtr clientid = generateClientId();
+ pkt->addOption(clientid);
+ if (srvid && (msg_type != DHCPV6_SOLICIT)) {
+ pkt->addOption(srvid);
+ }
+
+ pkt->addOption(OptionPtr(new Option6ClientFqdn(fqdn_flags,
+ fqdn_domain_name,
+ fqdn_type)));
+
+ if (include_oro) {
+ OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
+ D6O_ORO));
+ oro->addValue(D6O_CLIENT_FQDN);
+ pkt->addOption(oro);
+ }
+
+ return (pkt);
+ }
+
+ // Returns an instance of the option carrying FQDN.
+ Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
+ return (boost::dynamic_pointer_cast<Option6ClientFqdn>
+ (pkt->getOption(D6O_CLIENT_FQDN)));
+ }
+
+ void testFqdn(const uint16_t msg_type,
+ const bool use_oro,
+ const uint8_t in_flags,
+ const std::string& in_domain_name,
+ const Option6ClientFqdn::DomainNameType in_domain_type,
+ const uint8_t exp_flags,
+ const std::string& exp_domain_name) {
+ NakedDhcpv6Srv srv(0);
+ Pkt6Ptr question = generatePktWithFqdn(msg_type,
+ in_flags,
+ in_domain_name,
+ in_domain_type,
+ use_oro);
+ ASSERT_TRUE(getClientFqdnOption(question));
+
+ Pkt6Ptr answer;
+ if (msg_type == DHCPV6_SOLICIT) {
+ answer.reset(new Pkt6(DHCPV6_ADVERTISE, 1234));
+
+ } else {
+ answer.reset(new Pkt6(DHCPV6_REPLY, 1234));
+
+ }
+
+ ASSERT_NO_THROW(srv.processClientFqdn(question, answer));
+
+ Option6ClientFqdnPtr answ_fqdn = getClientFqdnOption(answer);
+ ASSERT_TRUE(answ_fqdn);
+
+ const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0 ?
+ true : false;
+ const bool flag_s = (exp_flags & Option6ClientFqdn::FLAG_S) != 0 ?
+ true : false;
+ const bool flag_o = (exp_flags & Option6ClientFqdn::FLAG_O) != 0 ?
+ true : false;
+
+ EXPECT_EQ(flag_n, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_N));
+ EXPECT_EQ(flag_s, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_S));
+ EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
+
+ EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
+ }
+};
+
// This test verifies that incoming SOLICIT can be handled properly when
// there are no subnets configured.
//
@@ -1756,6 +1854,50 @@ TEST_F(Dhcpv6SrvTest, ServerID) {
EXPECT_EQ(duid1_text, text);
}
+// A set of tests verifying server's behaviour when it receives the DHCPv6
+// Client Fqdn Option.
+// @todo: Extend these tests once appropriate configuration parameters are
+// implemented (ticket #3034).
+
+// Test server's response when client requests that server performs AAAA update.
+TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
+ testFqdn(DHCPV6_SOLICIT, true, FQDN_FLAG_S, "myhost.example.com",
+ Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
+ "myhost.example.com.");
+}
+
+// Test server's response when client provides partial domain-name and requests
+// that server performs AAAA update.
+TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
+ testFqdn(DHCPV6_SOLICIT, true, FQDN_FLAG_S, "myhost",
+ Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
+ "myhost.example.com.");
+}
+
+// Test server's response when client provides empty domain-name and requests
+// that server performs AAAA update.
+TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
+ testFqdn(DHCPV6_SOLICIT, true, FQDN_FLAG_S, "",
+ Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
+ "myhost.example.com.");
+}
+
+// Test server's response when client requests no DNS update.
+TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
+ testFqdn(DHCPV6_SOLICIT, true, FQDN_FLAG_N, "myhost.example.com",
+ Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
+ "myhost.example.com.");
+}
+
+// Test server's response when client requests that server delegates the AAAA
+// update to the client and this delegation is not allowed.
+TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
+ SCOPED_TRACE("Client AAAA Update is not allowed");
+ testFqdn(DHCPV6_SOLICIT, true, 0, "myhost.example.com.",
+ Option6ClientFqdn::FULL, FQDN_FLAG_S | FQDN_FLAG_O,
+ "myhost.example.com.");
+}
+
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
/// to call processX() methods.
diff --git a/src/lib/dhcp/option6_client_fqdn.cc b/src/lib/dhcp/option6_client_fqdn.cc
index a6dd2b0..1a1ba5d 100644
--- a/src/lib/dhcp/option6_client_fqdn.cc
+++ b/src/lib/dhcp/option6_client_fqdn.cc
@@ -75,8 +75,11 @@ Option6ClientFqdnImpl::Option6ClientFqdnImpl(OptionBufferConstIter first,
Option6ClientFqdnImpl::
Option6ClientFqdnImpl(const Option6ClientFqdnImpl& source)
: flags_(source.flags_),
- domain_name_(new isc::dns::Name(*source.domain_name_)),
+ domain_name_(),
domain_name_type_(source.domain_name_type_) {
+ if (source.domain_name_) {
+ domain_name_.reset(new isc::dns::Name(*source.domain_name_));
+ }
}
Option6ClientFqdnImpl&
@@ -260,6 +263,11 @@ Option6ClientFqdn::setFlag(const Flag flag, const bool set_flag) {
impl_->flags_ = new_flag;
}
+void
+Option6ClientFqdn::resetFlags() {
+ impl_->flags_ = 0;
+}
+
std::string
Option6ClientFqdn::getDomainName() const {
if (impl_->domain_name_) {
diff --git a/src/lib/dhcp/option6_client_fqdn.h b/src/lib/dhcp/option6_client_fqdn.h
index 1f2a478..bc139e3 100644
--- a/src/lib/dhcp/option6_client_fqdn.h
+++ b/src/lib/dhcp/option6_client_fqdn.h
@@ -170,6 +170,9 @@ public:
/// set (true), or cleared (false).
void setFlag(const Flag flag, const bool set);
+ /// @brief Sets the flag field value to 0.
+ void resetFlags();
+
/// @brief Returns the domain-name in the text format.
///
/// If domain-name is partial, it lacks the dot at the end (e.g. myhost).
diff --git a/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc b/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
index 4c26776..1587e25 100644
--- a/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
+++ b/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
@@ -392,6 +392,31 @@ TEST(Option6ClientFqdnTest, setFlag) {
InvalidFqdnOptionFlags);
}
+// This test verifies that flags field of the option is set to 0 when resetFlags
+// function is called.
+TEST(Option6ClientFqdnTest, resetFlags) {
+ boost::scoped_ptr<Option6ClientFqdn> option;
+ ASSERT_NO_THROW(
+ option.reset(new Option6ClientFqdn(Option6ClientFqdn::FLAG_S |
+ Option6ClientFqdn::FLAG_O,
+ "myhost.example.com",
+ Option6ClientFqdn::FULL))
+ );
+ ASSERT_TRUE(option);
+
+ // Check that flags we set in the constructor are set.
+ ASSERT_TRUE(option->getFlag(Option6ClientFqdn::FLAG_S));
+ ASSERT_TRUE(option->getFlag(Option6ClientFqdn::FLAG_O));
+ ASSERT_FALSE(option->getFlag(Option6ClientFqdn::FLAG_N));
+
+ option->resetFlags();
+
+ // After reset, all flags should be 0.
+ EXPECT_FALSE(option->getFlag(Option6ClientFqdn::FLAG_S));
+ EXPECT_FALSE(option->getFlag(Option6ClientFqdn::FLAG_O));
+ EXPECT_FALSE(option->getFlag(Option6ClientFqdn::FLAG_N));
+}
+
// This test verifies that current domain-name can be replaced with a new
// domain-name.
TEST(Option6ClientFqdnTest, setDomainName) {
More information about the bind10-changes
mailing list