diff options
Diffstat (limited to 'src/lib/dhcpsrv/d2_client.cc')
-rw-r--r-- | src/lib/dhcpsrv/d2_client.cc | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/d2_client.cc b/src/lib/dhcpsrv/d2_client.cc new file mode 100644 index 0000000000..494c858937 --- /dev/null +++ b/src/lib/dhcpsrv/d2_client.cc @@ -0,0 +1,263 @@ +// 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 +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include <dhcpsrv/d2_client.h> +#include <dhcpsrv/dhcpsrv_log.h> + +#include <string> + +using namespace std; + +namespace isc { +namespace dhcp { + +D2ClientConfig::D2ClientConfig(const bool enable_updates, + const isc::asiolink::IOAddress& server_ip, + const size_t server_port, + const dhcp_ddns:: + NameChangeProtocol& ncr_protocol, + const dhcp_ddns:: + NameChangeFormat& ncr_format, + const bool always_include_fqdn, + const bool override_no_update, + const bool override_client_update, + const bool replace_client_name, + const std::string& generated_prefix, + const std::string& qualifying_suffix) + : enable_updates_(enable_updates), + server_ip_(server_ip), + server_port_(server_port), + ncr_protocol_(ncr_protocol), + ncr_format_(ncr_format), + always_include_fqdn_(always_include_fqdn), + override_no_update_(override_no_update), + override_client_update_(override_client_update), + replace_client_name_(replace_client_name), + generated_prefix_(generated_prefix), + qualifying_suffix_(qualifying_suffix) { + validateContents(); +} + +D2ClientConfig::D2ClientConfig() + : enable_updates_(false), + server_ip_(isc::asiolink::IOAddress("0.0.0.0")), + server_port_(0), + ncr_protocol_(dhcp_ddns::NCR_UDP), + ncr_format_(dhcp_ddns::FMT_JSON), + always_include_fqdn_(false), + override_no_update_(false), + override_client_update_(false), + replace_client_name_(false), + generated_prefix_("myhost"), + qualifying_suffix_("example.com") { + validateContents(); +} + +D2ClientConfig::~D2ClientConfig(){}; + +void +D2ClientConfig::validateContents() { + if (ncr_format_ != dhcp_ddns::FMT_JSON) { + isc_throw(D2ClientError, "D2ClientConfig: NCR Format:" + << dhcp_ddns::ncrFormatToString(ncr_format_) + << " is not yet supported"); + } + + if (ncr_protocol_ != dhcp_ddns::NCR_UDP) { + isc_throw(D2ClientError, "D2ClientConfig: NCR Protocol:" + << dhcp_ddns::ncrProtocolToString(ncr_protocol_) + << " is not yet supported"); + } + + /// @todo perhaps more validation we should do yet? + /// Are there any invalid combinations of options we need to test against? +} + +bool +D2ClientConfig::operator == (const D2ClientConfig& other) const { + return ((enable_updates_ == other.enable_updates_) && + (server_ip_ == other.server_ip_) && + (server_port_ == other.server_port_) && + (ncr_protocol_ == other.ncr_protocol_) && + (ncr_format_ == other.ncr_format_) && + (always_include_fqdn_ == other.always_include_fqdn_) && + (override_no_update_ == other.override_no_update_) && + (override_client_update_ == other.override_client_update_) && + (replace_client_name_ == other.replace_client_name_) && + (generated_prefix_ == other.generated_prefix_) && + (qualifying_suffix_ == other.qualifying_suffix_)); +} + +bool +D2ClientConfig::operator != (const D2ClientConfig& other) const { + return (!(*this == other)); +} + +std::string +D2ClientConfig::toText() const { + std::ostringstream stream; + + stream << "enable_updates: " << (enable_updates_ ? "yes" : "no"); + if (enable_updates_) { + stream << ", server_ip: " << server_ip_.toText() + << ", server_port: " << server_port_ + << ", ncr_protocol: " << ncr_protocol_ + << ", ncr_format: " << ncr_format_ + << ", always_include_fqdn: " << (always_include_fqdn_ ? + "yes" : "no") + << ", override_no_update: " << (override_no_update_ ? + "yes" : "no") + << ", override_client_update: " << (override_client_update_ ? + "yes" : "no") + << ", replace_client_name: " << (replace_client_name_ ? + "yes" : "no") + << ", generated_prefix: [" << generated_prefix_ << "]" + << ", qualifying_suffix: [" << qualifying_suffix_ << "]"; + } + + return (stream.str()); +} + +std::ostream& +operator<<(std::ostream& os, const D2ClientConfig& config) { + os << config.toText(); + return (os); +} + +D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()) { + // Default constructor initializes with a disabled configuration. +} + +D2ClientMgr::~D2ClientMgr(){ +} + +void +D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) { + if (!new_config) { + isc_throw(D2ClientError, + "D2ClientMgr cannot set DHCP-DDNS configuration to NULL."); + } + + // @todo When NameChangeSender is integrated, we will need to handle these + // scenarios: + // 1. D2 was enabled but now it is disabled + // - destroy the sender, flush any queued + // 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 + // + // For now we just update the configuration. + d2_client_config_ = new_config; + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS) + .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" : + "DHCP_DDNS updates enabled"); +} + +bool +D2ClientMgr::ddnsEnabled() { + return (d2_client_config_->getEnableUpdates()); +} + +const D2ClientConfigPtr& +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 |