Commit 7f2ef410 authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[3282] Added FQDN processing logic to D2ClientMgr

Methods were added to D2ClientMgr which work for v4 and v6 to
determine the server values for FQDN flags and domain names based
upon the client FQDN and configuration options.
parent 1422c7b0
......@@ -62,8 +62,8 @@ D2ClientConfig::D2ClientConfig()
qualifying_suffix_("") {
qualifying_suffix_("") {
......@@ -142,7 +142,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.
......@@ -159,8 +159,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 +181,87 @@ D2ClientMgr::getD2ClientConfig() const {
return (d2_client_config_);
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 (secion 3.2)
// 0 1 client wants server to do forward updates (secion 3.3)
// 1 0 client wants no one to do updates (section 3.4)
// 1 1 invalid combination
// Make a bit mask from the client's flags and use it to set the response
// flags accordingly.
/// @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.
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() &&
case 1:
server_s = d2_client_config_->getEnableUpdates();
case 2:
// If updates are enabled and we are overriding "no updates" then
// S flag should be true.
server_s = (d2_client_config_->getEnableUpdates() &&
// In theory we cannot get here because the fqdn option class defends
// against this combination.
"Invalid client FQDN - N and S cannot both be 1");
server_n = !server_s;
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;
std::string tmp = gen_name.str();
return (qualifyName(tmp));
D2ClientMgr::qualifyName(const std::string& partial_name) const {
std::ostringstream gen_name;
gen_name << partial_name << "." << d2_client_config_->getQualifyingSuffix();
if (gen_name.str().back() != '.') {
gen_name << ".";
return (gen_name.str());
}; // namespace dhcp
}; // namespace isc
......@@ -189,7 +189,7 @@ 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.
......@@ -263,11 +263,130 @@ 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.
/// @param client_s S Flag from the client's FQDN
/// @param client_n N Flag from the client's FQDN
/// @param server_s S Flag for the server's FQDN (output)
/// @param server_n N Flag for the server's FQDN (output)
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.
/// @param fqdn FQDN option from which to read client (inbound) flags
/// @param fqdn_resp FQDN option to update with the server (outbound) flags
template <class OPT>
void adjustFqdnFlags(OPT& fqdn, OPT& 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
template <class OPT>
void adjustDomainName(OPT& fqdn, OPT& fqdn_resp);
/// @brief Container class for DHCP-DDNS configuration parameters.
D2ClientConfigPtr d2_client_config_;
template <class OPT>
D2ClientMgr::adjustFqdnFlags(OPT& fqdn, OPT& fqdn_resp) {
bool server_s = false;
bool server_n = false;
analyzeFqdn(fqdn.getFlag(OPT::FLAG_S), fqdn.getFlag(OPT::FLAG_N),
server_s, server_n);
// Reset the flags to zero to avoid triggering N and S both 1 check.
// Set S and N flags.
fqdn_resp.setFlag(OPT::FLAG_S, server_s);
fqdn_resp.setFlag(OPT::FLAG_N, server_n);
// Set O flag true if server S overrides client S.
fqdn_resp.setFlag(OPT::FLAG_O, (fqdn.getFlag(OPT::FLAG_S) != server_s));
template <class OPT>
D2ClientMgr::adjustDomainName(OPT& fqdn, OPT& 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("", OPT::PARTIAL);
} else {
// If the supplied name is partial, qualify it by adding the suffix.
if (fqdn.getDomainNameType() == OPT::PARTIAL) {
std::ostringstream name;
name << fqdn.getDomainName();
name << "." << (d2_client_config_->getQualifyingSuffix());
if (d2_client_config_->getQualifyingSuffix().back() != '.') {
name << ".";
fqdn_resp.setDomainName(name.str(), OPT::FULL);
/// @brief Defines a pointer for D2ClientMgr instances.
typedef boost::shared_ptr<D2ClientMgr> D2ClientMgrPtr;
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment