diff options
Diffstat (limited to 'src/lib/dhcpsrv')
31 files changed, 3015 insertions, 777 deletions
diff --git a/src/lib/dhcpsrv/.gitignore b/src/lib/dhcpsrv/.gitignore index 0b02c01a50..1f085382ce 100644 --- a/src/lib/dhcpsrv/.gitignore +++ b/src/lib/dhcpsrv/.gitignore @@ -1,2 +1,3 @@ /dhcpsrv_messages.cc /dhcpsrv_messages.h +/s-messages diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index b3c420680f..69b14b2ee0 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -39,6 +39,7 @@ libb10_dhcpsrv_la_SOURCES = libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h libb10_dhcpsrv_la_SOURCES += callout_handle_store.h +libb10_dhcpsrv_la_SOURCES += d2_client.cc d2_client.h libb10_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h libb10_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h @@ -64,6 +65,7 @@ libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS) libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES) libb10_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la +libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index dd1481e9e1..11b0700e3a 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.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 @@ -90,7 +90,7 @@ AllocEngine::IterativeAllocator::increasePrefix(const isc::asiolink::IOAddress& const uint8_t prefix_len) { if (!prefix.isV6()) { isc_throw(BadValue, "Prefix operations are for IPv6 only (attempted to " - "increase prefix " << prefix.toText() << ")"); + "increase prefix " << prefix << ")"); } // Get a buffer holding an address. @@ -294,11 +294,12 @@ AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts, Lease6Collection AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, - uint32_t iaid, const IOAddress& hint, + const uint32_t iaid, const IOAddress& hint, Lease::Type type, const bool fwd_dns_update, const bool rev_dns_update, const std::string& hostname, bool fake_allocation, - const isc::hooks::CalloutHandlePtr& callout_handle) { + const isc::hooks::CalloutHandlePtr& callout_handle, + Lease6Collection& old_leases) { try { AllocatorPtr allocator = getAllocator(type); @@ -316,37 +317,49 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, isc_throw(InvalidOperation, "DUID is mandatory for allocation"); } - // check if there's existing lease for that subnet/duid/iaid combination. + // Check if there's existing lease for that subnet/duid/iaid + // combination. /// @todo: Make this generic (cover temp. addrs and prefixes) Lease6Collection existing = LeaseMgrFactory::instance().getLeases6(type, *duid, iaid, subnet->getID()); + // There is at least one lease for this client. We will return these + // leases for the client, but we may need to update FQDN information. if (!existing.empty()) { - // we have at least one lease already. This is a returning client, - // probably after his reboot. - return (existing); + // Return old leases so the server can see what has changed. + old_leases = existing; + return (updateFqdnData(existing, fwd_dns_update, rev_dns_update, + hostname, fake_allocation)); } // check if the hint is in pool and is available // This is equivalent of subnet->inPool(hint), but returns the pool - Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(subnet->getPool(type, hint, false)); + Pool6Ptr pool = boost::dynamic_pointer_cast< + Pool6>(subnet->getPool(type, hint, false)); if (pool) { /// @todo: We support only one hint for now Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(type, hint); if (!lease) { - /// @todo: check if the hint is reserved once we have host support - /// implemented - - // the hint is valid and not currently used, let's create a lease for it - lease = createLease6(subnet, duid, iaid, hint, pool->getLength(), - type, fwd_dns_update, rev_dns_update, + /// @todo: check if the hint is reserved once we have host + /// support implemented + + // The hint is valid and not currently used, let's create a + // lease for it + lease = createLease6(subnet, duid, iaid, hint, + pool->getLength(), type, + fwd_dns_update, rev_dns_update, hostname, callout_handle, fake_allocation); - // It can happen that the lease allocation failed (we could have lost - // the race condition. That means that the hint is lo longer usable and - // we need to continue the regular allocation path. + // It can happen that the lease allocation failed (we could + // have lost the race condition. That means that the hint is + // lo longer usable and we need to continue the regular + // allocation path. if (lease) { + // We are allocating a new lease (not renewing). So, the + // old lease should be NULL. + old_leases.push_back(Lease6Ptr()); + /// @todo: We support only one lease per ia for now Lease6Collection collection; collection.push_back(lease); @@ -354,6 +367,11 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, } } else { if (lease->expired()) { + // Copy an existing, expired lease so as it can be returned + // to the caller. + Lease6Ptr old_lease(new Lease6(*lease)); + old_leases.push_back(old_lease); + /// We found a lease and it is expired, so we can reuse it lease = reuseExpiredLease(lease, subnet, duid, iaid, pool->getLength(), @@ -414,6 +432,10 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, rev_dns_update, hostname, callout_handle, fake_allocation); if (lease) { + // We are allocating a new lease (not renewing). So, the + // old lease should be NULL. + old_leases.push_back(Lease6Ptr()); + Lease6Collection collection; collection.push_back(lease); return (collection); @@ -424,6 +446,11 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, // allocation attempts. } else { if (existing->expired()) { + // Copy an existing, expired lease so as it can be returned + // to the caller. + Lease6Ptr old_lease(new Lease6(*existing)); + old_leases.push_back(old_lease); + existing = reuseExpiredLease(existing, subnet, duid, iaid, prefix_len, fwd_dns_update, rev_dns_update, hostname, @@ -1037,6 +1064,30 @@ Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet, } } +Lease6Collection +AllocEngine::updateFqdnData(const Lease6Collection& leases, + const bool fwd_dns_update, + const bool rev_dns_update, + const std::string& hostname, + const bool fake_allocation) { + Lease6Collection updated_leases; + for (Lease6Collection::const_iterator lease_it = leases.begin(); + lease_it != leases.end(); ++lease_it) { + Lease6Ptr lease(new Lease6(**lease_it)); + lease->fqdn_fwd_ = fwd_dns_update; + lease->fqdn_rev_ = rev_dns_update; + lease->hostname_ = hostname; + if (!fake_allocation && + ((lease->fqdn_fwd_ != (*lease_it)->fqdn_fwd_) || + (lease->fqdn_rev_ != (*lease_it)->fqdn_rev_) || + (lease->hostname_ != (*lease_it)->hostname_))) { + LeaseMgrFactory::instance().updateLease6(lease); + } + updated_leases.push_back(lease); + } + return (updated_leases); +} + AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) { std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type); diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h index 8299bb8512..ed2a767266 100644 --- a/src/lib/dhcpsrv/alloc_engine.h +++ b/src/lib/dhcpsrv/alloc_engine.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 @@ -338,14 +338,21 @@ protected: /// an address for SOLICIT that is not really allocated (true) /// @param callout_handle a callout handle (used in hooks). A lease callouts /// will be executed if this parameter is passed. + /// @param [out] old_leases Collection to which this function will append + /// old leases. Leases are stored in the same order as in the + /// collection of new leases, being returned. For newly allocated + /// leases (not renewed) the NULL pointers are stored in this + /// collection as old leases. /// /// @return Allocated IPv6 leases (may be empty if allocation failed) Lease6Collection - allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, uint32_t iaid, + allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, + const uint32_t iaid, const isc::asiolink::IOAddress& hint, Lease::Type type, const bool fwd_dns_update, const bool rev_dns_update, const std::string& hostname, bool fake_allocation, - const isc::hooks::CalloutHandlePtr& callout_handle); + const isc::hooks::CalloutHandlePtr& callout_handle, + Lease6Collection& old_leases); /// @brief returns allocator for a given pool type /// @param type type of pool (V4, IA, TA or PD) @@ -489,6 +496,28 @@ private: const isc::hooks::CalloutHandlePtr& callout_handle, bool fake_allocation = false); + /// @brief Updates FQDN data for a collection of leases. + /// + /// @param leases Collection of leases for which FQDN data should be + /// updated. + /// @param fwd_dns_update Boolean value which indicates whether forward FQDN + /// update was performed for each lease (true) or not (false). + /// @param rev_dns_update Boolean value which indicates whether reverse FQDN + /// update was performed for each lease (true) or not (false). + /// @param hostname Client hostname associated with a lease. + /// @param fake_allocation Boolean value which indicates that it is a real + /// lease allocation, e.g. Request message is processed (false), or address + /// is just being picked as a result of processing Solicit (true). In the + /// latter case, the FQDN data should not be updated in the lease database. + /// + /// @return Collection of leases with updated FQDN data. Note that returned + /// collection holds updated FQDN data even for fake allocation. + Lease6Collection updateFqdnData(const Lease6Collection& leases, + const bool fwd_dns_update, + const bool rev_dns_update, + const std::string& hostname, + const bool fake_allocation); + /// @brief a pointer to currently used allocator /// /// For IPv4, there will be only one allocator: TYPE_V4 diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc index 7789c74454..798d508f12 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 @@ -152,7 +152,7 @@ CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) { // configuration. Such requirement makes sense in IPv4, but not in IPv6. // The server does not need to have a global address (using just link-local // is ok for DHCPv6 server) from the pool it serves. - if ((subnets6_.size() == 1) && hint.getAddress().to_v6().is_link_local()) { + if ((subnets6_.size() == 1) && hint.isV6LinkLocal()) { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ONLY_SUBNET6) .arg(subnets6_[0]->toText()).arg(hint.toText()); @@ -348,9 +348,30 @@ CfgMgr::getUnicast(const std::string& iface) const { return (&(*addr).second); } +void +CfgMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) { + d2_client_mgr_.setD2ClientConfig(new_config); +} + +bool +CfgMgr::ddnsEnabled() { + return (d2_client_mgr_.ddnsEnabled()); +} + +const D2ClientConfigPtr& +CfgMgr::getD2ClientConfig() const { + return (d2_client_mgr_.getD2ClientConfig()); +} + +D2ClientMgr& +CfgMgr::getD2ClientMgr() { + return (d2_client_mgr_); +} + CfgMgr::CfgMgr() : datadir_(DHCP_DATA_DIR), - all_ifaces_active_(false), echo_v4_client_id_(true) { + all_ifaces_active_(false), echo_v4_client_id_(true), + d2_client_mgr_() { // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am // Note: the definition of DHCP_DATA_DIR needs to include quotation marks // See AM_CPPFLAGS definition in Makefile.am diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h index cda59f2c58..18455d08f2 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 @@ -19,6 +19,7 @@ #include <dhcp/option.h> #include <dhcp/option_definition.h> #include <dhcp/option_space.h> +#include <dhcpsrv/d2_client.h> #include <dhcpsrv/option_space_container.h> #include <dhcpsrv/pool.h> #include <dhcpsrv/subnet.h> @@ -332,6 +333,29 @@ public: return (echo_v4_client_id_); } + /// @brief Updates the DHCP-DDNS client configuration to the given value. + /// + /// @param new_config pointer to the new client configuration. + /// + /// @throw Underlying method(s) will throw D2ClientError if given an empty + /// pointer. + void setD2ClientConfig(D2ClientConfigPtr& new_config); + + /// @brief Convenience method for checking if DHCP-DDNS updates are enabled. + /// + /// @return True if the D2 configuration is enabled. + bool ddnsEnabled(); + + /// @brief Fetches the DHCP-DDNS configuration pointer. + /// + /// @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. @@ -411,6 +435,9 @@ private: /// Indicates whether v4 server should send back client-id bool echo_v4_client_id_; + + /// @brief Manages the DHCP-DDNS client and its configuration. + D2ClientMgr d2_client_mgr_; }; } // namespace isc::dhcp 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 diff --git a/src/lib/dhcpsrv/d2_client.h b/src/lib/dhcpsrv/d2_client.h new file mode 100644 index 0000000000..0a6faa4713 --- /dev/null +++ b/src/lib/dhcpsrv/d2_client.h @@ -0,0 +1,382 @@ +// 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. + +#ifndef D2_CLIENT_H +#define D2_CLIENT_H + +/// @file d2_client.h Defines the D2ClientConfig and D2ClientMgr classes. +/// This file defines the classes Kea uses to act as a client of the b10- +/// dhcp-ddns module (aka D2). +/// +#include <asiolink/io_address.h> +#include <dhcp_ddns/ncr_io.h> +#include <exceptions/exceptions.h> + +#include <boost/shared_ptr.hpp> + +#include <stdint.h> +#include <string> +#include <vector> + +namespace isc { +namespace dhcp { + + +/// An exception that is thrown if an error occurs while configuring +/// the D2 DHCP DDNS client. +class D2ClientError : public isc::Exception { +public: + + /// @brief constructor + /// + /// @param file name of the file, where exception occurred + /// @param line line of the file, where exception occurred + /// @param what text description of the issue that caused exception + D2ClientError(const char* file, size_t line, const char* what) + : isc::Exception(file, line, what) {} +}; + +/// @brief Acts as a storage vault for D2 client configuration +/// +/// A simple container class for storing and retrieving the configuration +/// parameters associated with DHCP-DDNS and acting as a client of D2. +/// Instances of this class may be constructed through configuration parsing. +/// +class D2ClientConfig { +public: + /// @brief Constructor + /// + /// @param enable_updates Enables DHCP-DDNS updates + /// @param server_ip IP address of the b10-dhcp-ddns server (IPv4 or IPv6) + /// @param server_port IP port of the b10-dhcp-ddns server + /// @param ncr_protocol Socket protocol to use with b10-dhcp-ddns + /// Currently only UDP is supported. + /// @param ncr_format Format of the b10-dhcp-ddns requests. + /// Currently only JSON format is supported. + /// @param always_include_fqdn Enables always including the FQDN option in + /// DHCP responses. + /// @param override_no_update Enables updates, even if clients request no + /// updates. + /// @param override_client_update Perform updates, even if client requested + /// delegation. + /// @param replace_client_name enables replacement of the domain-name + /// supplied by the client with a generated name. + /// @param generated_prefix Prefix to use when generating domain-names. + /// @param qualifying_suffix Suffix to use to qualify partial domain-names. + /// + /// @throw D2ClientError if given an invalid protocol or format. + 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); + + /// @brief Default constructor + /// The default constructor creates an instance that has updates disabled. + D2ClientConfig(); + + /// @brief Destructor + virtual ~D2ClientConfig(); + + /// @brief Return whether or not DHCP-DDNS updating is enabled. + bool getEnableUpdates() const { + return(enable_updates_); + } + + /// @brief Return the IP address of b10-dhcp-ddns (IPv4 or IPv6). + const isc::asiolink::IOAddress& getServerIp() const { + return(server_ip_); + } + + /// @brief Return the IP port of b10-dhcp-ddns. + size_t getServerPort() const { + return(server_port_); + } + + /// @brief Return the socket protocol to use with b10-dhcp-ddns. + const dhcp_ddns::NameChangeProtocol& getNcrProtocol() const { + return(ncr_protocol_); + } + + /// @brief Return the b10-dhcp-ddns request format. + const dhcp_ddns::NameChangeFormat& getNcrFormat() const { + return(ncr_format_); + } + + /// @brief Return whether or not FQDN is always included in DHCP responses. + bool getAlwaysIncludeFqdn() const { + return(always_include_fqdn_); + } + + /// @brief Return if updates are done even if clients request no updates. + bool getOverrideNoUpdate() const { + return(override_no_update_); + } + + /// @brief Return if updates are done even when clients request delegation. + bool getOverrideClientUpdate() const { + return(override_client_update_); + } + + /// @brief Return whether or not client's domain-name is always replaced. + bool getReplaceClientName() const { + return(replace_client_name_); + } + + /// @brief Return the prefix to use when generating domain-names. + const std::string& getGeneratedPrefix() const { + return(generated_prefix_); + } + + /// @brief Return the suffix to use to qualify partial domain-names. + const std::string& getQualifyingSuffix() const { + return(qualifying_suffix_); + } + + /// @brief Compares two D2ClientConfigs for equality + bool operator == (const D2ClientConfig& other) const; + + /// @brief Compares two D2ClientConfigs for inequality + bool operator != (const D2ClientConfig& other) const; + + /// @brief Generates a string representation of the class contents. + std::string toText() const; + +protected: + /// @brief Validates member values. + /// + /// Method is used by the constructor to validate member contents. + /// + /// @throw D2ClientError if given an invalid protocol or format. + virtual void validateContents(); + +private: + /// @brief Indicates whether or not DHCP DDNS updating is enabled. + bool enable_updates_; + + /// @brief IP address of the b10-dhcp-ddns server (IPv4 or IPv6). + isc::asiolink::IOAddress server_ip_; + + /// @brief IP port of the b10-dhcp-ddns server. + size_t server_port_; + + /// @brief The socket protocol to use with b10-dhcp-ddns. + /// 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 always include the FQDN option in its response. + bool always_include_fqdn_; + + /// @brief Should Kea perform updates, even if client requested no updates. + /// Overrides the client request for no updates via the N flag. + bool override_no_update_; + + /// @brief Should Kea perform updates, even if client requested delegation. + bool override_client_update_; + + /// @brief Should Kea replace the domain-name supplied by the client. + bool replace_client_name_; + + /// @brief Prefix Kea should use when generating domain-names. + std::string generated_prefix_; + + /// @brief Suffix Kea should use when to qualify partial domain-names. + std::string qualifying_suffix_; +}; + +std::ostream& +operator<<(std::ostream& os, const D2ClientConfig& config); + +/// @brief Defines a pointer for D2ClientConfig instances. +typedef boost::shared_ptr<D2ClientConfig> D2ClientConfigPtr; + +/// @brief D2ClientMgr isolates Kea from the details of being a D2 client. +/// +/// Provides services for managing the current D2ClientConfig and managing +/// communications with D2. (@todo The latter will be added once communication +/// with D2 is implemented through the integration of +/// dhcp_ddns::NameChangeSender interface(s)). +/// +class D2ClientMgr { +public: + /// @brief Constructor + /// + /// Default constructor which constructs an instance which has DHCP-DDNS + /// updates disabled. + D2ClientMgr(); + + /// @brief Destructor. + ~D2ClientMgr(); + + /// @brief Updates the DHCP-DDNS client configuration to the given value. + /// + /// @param new_config pointer to the new client configuration. + /// @throw D2ClientError if passed an empty pointer. + void setD2ClientConfig(D2ClientConfigPtr& new_config); + + /// @brief Convenience method for checking if DHCP-DDNS is enabled. + /// + /// @return True if the D2 configuration is enabled. + bool ddnsEnabled(); + + /// @brief Fetches the DHCP-DDNS configuration pointer. + /// + /// @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; + + +} // namespace isc +} // namespace dhcp + +#endif diff --git a/src/lib/dhcpsrv/dbaccess_parser.h b/src/lib/dhcpsrv/dbaccess_parser.h index 53e3f81ea3..77c984554d 100644 --- a/src/lib/dhcpsrv/dbaccess_parser.h +++ b/src/lib/dhcpsrv/dbaccess_parser.h @@ -94,7 +94,8 @@ public: /// /// Creates an instance of this parser. /// - /// @param name Name of the parameter used to access the configuration. + /// @param param_name Name of the parameter used to access the + /// configuration. /// /// @return Pointer to a DbAccessParser. The caller is responsible for /// destroying the parser after use. diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc index ffd49d3083..7aea31032e 100644 --- a/src/lib/dhcpsrv/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/dhcp_parsers.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 @@ -400,15 +400,27 @@ void OptionDataParser::createOption() { // Option code is held in the uint32_t storage but is supposed to // be uint16_t value. We need to check that value in the configuration - // does not exceed range of uint8_t and is not zero. + // does not exceed range of uint8_t for DHCPv4, uint16_t for DHCPv6 and + // is not zero. uint32_t option_code = uint32_values_->getParam("code"); if (option_code == 0) { isc_throw(DhcpConfigError, "option code must not be zero." - << " Option code '0' is reserved in DHCPv4."); - } else if (option_code > std::numeric_limits<uint8_t>::max()) { + << " Option code '0' is reserved."); + + } else if (global_context_->universe_ == Option::V4 && + option_code > std::numeric_limits<uint8_t>::max()) { + isc_throw(DhcpConfigError, "invalid option code '" << option_code + << "', it must not exceed '" + << static_cast<int>(std::numeric_limits<uint8_t>::max()) + << "'"); + + } else if (global_context_->universe_ == Option::V6 && + option_code > std::numeric_limits<uint16_t>::max()) { isc_throw(DhcpConfigError, "invalid option code '" << option_code << "', it must not exceed '" - << std::numeric_limits<uint8_t>::max() << "'"); + << std::numeric_limits<uint16_t>::max() + << "'"); + } // Check that the option name has been specified, is non-empty and does not @@ -464,7 +476,7 @@ OptionDataParser::createOption() { } // Get option data from the configuration database ('data' field). - const std::string option_data = string_values_->getParam("data"); + std::string option_data = string_values_->getParam("data"); // Transform string of hexadecimal digits into binary format. std::vector<uint8_t> binary; @@ -480,6 +492,12 @@ OptionDataParser::createOption() { // Otherwise, the option data is specified as a string of // hexadecimal digits that we have to turn into binary format. try { + // The decodeHex function expects that the string contains an + // even number of digits. If we don't meet this requirement, + // we have to insert a leading 0. + if (!option_data.empty() && option_data.length() % 2) { + option_data = option_data.insert(0, "0"); + } util::encode::decodeHex(option_data, binary); } catch (...) { isc_throw(DhcpConfigError, "option data is not a valid" @@ -1163,5 +1181,108 @@ SubnetConfigParser::getParam(const std::string& name) { return (Triplet<uint32_t>(value)); } +//**************************** D2ClientConfigParser ********************** +D2ClientConfigParser::D2ClientConfigParser(const std::string& entry_name) + : entry_name_(entry_name), boolean_values_(new BooleanStorage()), + uint32_values_(new Uint32Storage()), string_values_(new StringStorage()), + local_client_config_() { +} + +D2ClientConfigParser::~D2ClientConfigParser() { +} + +void +D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) { + BOOST_FOREACH(ConfigPair param, client_config->mapValue()) { + ParserPtr parser(createConfigParser(param.first)); + parser->build(param.second); + parser->commit(); + } + + bool enable_updates = boolean_values_->getParam("enable-updates"); + 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; + } + + // Get all parameters that are needed to create the D2ClientConfig. + asiolink::IOAddress server_ip(string_values_->getParam("server-ip")); + + uint32_t server_port = uint32_values_->getParam("server-port"); + + dhcp_ddns::NameChangeProtocol + ncr_protocol = dhcp_ddns:: stringToNcrProtocol(string_values_-> + getParam("ncr-protocol")); + + dhcp_ddns::NameChangeFormat + ncr_format = dhcp_ddns::stringToNcrFormat(string_values_-> + getParam("ncr-format")); + + std::string generated_prefix = string_values_->getParam("generated-prefix"); + std::string qualifying_suffix = string_values_-> + getParam("qualifying-suffix"); + + 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_-> + getParam("override-client-update"); + bool replace_client_name = boolean_values_->getParam("replace-client-name"); + + // Attempt to create the new client config. + local_client_config_.reset(new D2ClientConfig(enable_updates, server_ip, + server_port, ncr_protocol, + ncr_format, + always_include_fqdn, + override_no_update, + override_client_update, + replace_client_name, + generated_prefix, + qualifying_suffix)); +} + +isc::dhcp::ParserPtr +D2ClientConfigParser::createConfigParser(const std::string& config_id) { + DhcpConfigParser* parser = NULL; + if (config_id.compare("server-port") == 0) { + parser = new Uint32Parser(config_id, uint32_values_); + } else if ((config_id.compare("server-ip") == 0) || + (config_id.compare("ncr-protocol") == 0) || + (config_id.compare("ncr-format") == 0) || + (config_id.compare("generated-prefix") == 0) || + (config_id.compare("qualifying-suffix") == 0)) { + parser = new StringParser(config_id, string_values_); + } else if ((config_id.compare("enable-updates") == 0) || + (config_id.compare("always-include-fqdn") == 0) || + (config_id.compare("allow-client-update") == 0) || + (config_id.compare("override-no-update") == 0) || + (config_id.compare("override-client-update") == 0) || + (config_id.compare("replace-client-name") == 0)) { + parser = new BooleanParser(config_id, boolean_values_); + } else { + isc_throw(NotImplemented, + "parser error: D2ClientConfig parameter not supported: " + << config_id); + } + + return (isc::dhcp::ParserPtr(parser)); +} + +void +D2ClientConfigParser::commit() { + // @todo if local_client_config_ is empty then shutdown the listener... + // @todo Should this also attempt to start a listener? + // In keeping with Interface, Subnet, and Hooks parsers, then this + // should initialize the listener. Failure to init it, should cause + // rollback. This gets sticky, because who owns the listener instance? + // Does CfgMgr maintain it or does the server class? If the latter + // how do we get that value here? + // I'm thinkikng D2ClientConfig could contain the listener instance + CfgMgr::instance().setD2ClientConfig(local_client_config_); +} + }; // namespace dhcp }; // namespace isc diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h index 28c57b8b93..00a07a0a3a 100644 --- a/src/lib/dhcpsrv/dhcp_parsers.h +++ b/src/lib/dhcpsrv/dhcp_parsers.h @@ -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 @@ -18,6 +18,7 @@ #include <asiolink/io_address.h> #include <cc/data.h> #include <dhcp/option_definition.h> +#include <dhcpsrv/d2_client.h> #include <dhcpsrv/dhcp_config_parser.h> #include <dhcpsrv/option_space_container.h> #include <dhcpsrv/subnet.h> @@ -243,7 +244,7 @@ public: // its value. If it doesn't we insert a new element. storage_->setParam(param_name_, value_); } - + private: /// Pointer to the storage where committed value is stored. boost::shared_ptr<ValueStorage<ValueType> > storage_; @@ -876,6 +877,77 @@ protected: ParserContextPtr global_context_; }; +/// @brief Parser for D2ClientConfig +/// +/// This class parses the configuration element "dhcp-ddns" common to the +/// spec files for both dhcp4 and dhcp6. It creates an instance of a +/// D2ClientConfig. +class D2ClientConfigParser : public isc::dhcp::DhcpConfigParser { +public: + /// @brief Constructor + /// + /// @param entry_name is an arbitrary label assigned to this configuration + /// definition. + D2ClientConfigParser(const std::string& entry_name); + + /// @brief Destructor + virtual ~D2ClientConfigParser(); + + /// @brief Performs the parsing of the given dhcp-ddns element. + /// + /// The results of the parsing are retained internally for use during + /// commit. + /// + /// @param client_config is the "dhcp-ddns" configuration to parse + virtual void build(isc::data::ConstElementPtr client_config); + + /// @brief Creates a parser for the given "dhcp-ddns" member element id. + /// + /// The elements currently supported are (see isc::dhcp::D2ClientConfig + /// for details on each): + /// -# enable-updates + /// -# server-ip + /// -# server-port + /// -# ncr-protocol + /// -# ncr-format + /// -# remove-on-renew + /// -# always-include-fqdn + /// -# allow-client-update + /// -# override-no-update + /// -# override-client-update + /// -# replace-client-name + /// -# generated-prefix + /// -# qualifying-suffix + /// + /// @param config_id is the "item_name" for a specific member element of + /// the "dns_server" specification. + /// + /// @return returns a pointer to newly created parser. + virtual isc::dhcp::ParserPtr createConfigParser(const std::string& + config_id); + + /// @brief Instantiates a D2ClientConfig from internal data values + /// passes to CfgMgr singleton. + virtual void commit(); + +private: + /// @brief Arbitrary label assigned to this parser instance. + /// Primarily used for diagnostics. + std::string entry_name_; + + /// Storage for subnet-specific boolean values. + BooleanStoragePtr boolean_values_; + + /// Storage for subnet-specific integer values. + Uint32StoragePtr uint32_values_; + + /// Storage for subnet-specific string values. + StringStoragePtr string_values_; + + /// @brief Pointer to temporary local instance created during build. + D2ClientConfigPtr local_client_config_ ; +}; + // Pointers to various parser objects. typedef boost::shared_ptr<BooleanParser> BooleanParserPtr; typedef boost::shared_ptr<StringParser> StringParserPtr; diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 6ee3e87de7..5c7854e4d1 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -70,6 +70,9 @@ specified IPv6 subnet to its database. A debug message issued when server is being configured to listen on all interfaces. +% DHCPSRV_CFGMGR_CFG_DHCP_DDNS Setting DHCP-DDNS configuration to: %1 +A debug message issued when the server's DHCP-DDNS settings are changed. + % DHCPSRV_CFGMGR_CLEAR_ACTIVE_IFACES stop listening on all interfaces A debug message issued when configuration manager clears the internal list of active interfaces. This doesn't prevent the server from listening to diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc index 4ca6a3ce0b..7bc71f94d8 100644 --- a/src/lib/dhcpsrv/lease.cc +++ b/src/lib/dhcpsrv/lease.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 @@ -56,6 +56,13 @@ bool Lease::expired() const { return (expire_time < time(NULL)); } +bool +Lease::hasIdenticalFqdn(const Lease& other) const { + return (hostname_ == other.hostname_ && + fqdn_fwd_ == other.fqdn_fwd_ && + fqdn_rev_ == other.fqdn_rev_); +} + Lease4::Lease4(const Lease4& other) : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_, other.subnet_id_, other.cltt_, other.fqdn_fwd_, @@ -175,7 +182,7 @@ Lease6::toText() const { stream << "Type: " << typeToText(type_) << "(" << static_cast<int>(type_) << ") "; - stream << "Address: " << addr_.toText() << "\n" + stream << "Address: " << addr_ << "\n" << "Prefix length: " << static_cast<int>(prefixlen_) << "\n" << "IAID: " << iaid_ << "\n" << "Pref life: " << preferred_lft_ << "\n" @@ -190,7 +197,7 @@ std::string Lease4::toText() const { ostringstream stream; - stream << "Address: " << addr_.toText() << "\n" + stream << "Address: " << addr_ << "\n" << "Valid life: " << valid_lft_ << "\n" << "T1: " << t1_ << "\n" << "T2: " << t2_ << "\n" diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h index 86266205c9..c767142761 100644 --- a/src/lib/dhcpsrv/lease.h +++ b/src/lib/dhcpsrv/lease.h @@ -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 @@ -141,6 +141,14 @@ struct Lease { /// @return true if the lease is expired bool expired() const; + /// @brief Returns true if the other lease has equal FQDN data. + /// + /// @param other Lease which FQDN data is to be compared with our lease. + /// + /// @return Boolean value which indicates whether FQDN data of the other + /// lease is equal to the FQDN data of our lease (true) or not (false). + bool hasIdenticalFqdn(const Lease& other) const; + }; /// @brief Structure that holds a lease for IPv4 address diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index 9355736de3..aab7b9ed05 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -214,7 +214,7 @@ public: /// @param subnet_id A subnet identifier. /// /// @return A pointer to the lease or NULL if the lease is not found. - virtual Lease4Ptr getLease4(const ClientId& clientid, const HWAddr& hwaddr, + virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr, SubnetID subnet_id) const = 0; /// @brief Returns existing IPv4 lease for specified client-id diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc index dbc3bdd5ce..c6c9c9feb1 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.cc +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -247,7 +247,7 @@ Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) { Lease4Storage::iterator lease_it = storage4_.find(lease->addr_); if (lease_it == storage4_.end()) { isc_throw(NoSuchLease, "failed to update the lease with address " - << lease->addr_.toText() << " - no such lease"); + << lease->addr_ << " - no such lease"); } **lease_it = *lease; } @@ -260,7 +260,7 @@ Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) { Lease6Storage::iterator lease_it = storage6_.find(lease->addr_); if (lease_it == storage6_.end()) { isc_throw(NoSuchLease, "failed to update the lease with address " - << lease->addr_.toText() << " - no such lease"); + << lease->addr_ << " - no such lease"); } **lease_it = *lease; } diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 623440bf22..c006bbf5a5 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -1809,12 +1809,12 @@ MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind, int affected_rows = mysql_stmt_affected_rows(statements_[stindex]); if (affected_rows == 0) { isc_throw(NoSuchLease, "unable to update lease for address " << - lease->addr_.toText() << " as it does not exist"); + lease->addr_ << " as it does not exist"); } else if (affected_rows > 1) { // Should not happen - primary key constraint should only have selected // one row. isc_throw(DbOperationError, "apparently updated more than one lease " - "that had the address " << lease->addr_.toText()); + "that had the address " << lease->addr_); } } diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc index f1ba871aa5..d9c3da0e4d 100644 --- a/src/lib/dhcpsrv/pool.cc +++ b/src/lib/dhcpsrv/pool.cc @@ -34,8 +34,8 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const { std::string Pool::toText() const { std::stringstream tmp; - tmp << "type=" << Lease::typeToText(type_) << ", " << first_.toText() - << "-" << last_.toText(); + tmp << "type=" << Lease::typeToText(type_) << ", " << first_ + << "-" << last_; return (tmp.str()); } @@ -143,8 +143,8 @@ Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix, std::string Pool6::toText() const { std::stringstream tmp; - tmp << "type=" << Lease::typeToText(type_) << ", " << first_.toText() - << "-" << last_.toText() << ", delegated_len=" + tmp << "type=" << Lease::typeToText(type_) << ", " << first_ + << "-" << last_ << ", delegated_len=" << static_cast<int>(prefix_len_); return (tmp.str()); } diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index d861afe25f..0134d8c9ac 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -24,11 +24,14 @@ using namespace isc::asiolink; namespace isc { namespace dhcp { +// This is an initial value of subnet-id. See comments in subnet.h for details. +SubnetID Subnet::static_id_ = 1; + Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len, const Triplet<uint32_t>& t1, const Triplet<uint32_t>& t2, const Triplet<uint32_t>& valid_lifetime) - :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1), + :id_(generateNextID()), prefix_(prefix), prefix_len_(len), t1_(t1), t2_(t2), valid_(valid_lifetime), last_allocated_ia_(lastAddrInPrefix(prefix, len)), last_allocated_ta_(lastAddrInPrefix(prefix, len)), @@ -162,7 +165,7 @@ void Subnet::setLastAllocated(Lease::Type type, std::string Subnet::toText() const { std::stringstream tmp; - tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_); + tmp << prefix_ << "/" << static_cast<unsigned int>(prefix_len_); return (tmp.str()); } @@ -187,7 +190,7 @@ Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length, void Subnet4::setSiaddr(const isc::asiolink::IOAddress& siaddr) { if (!siaddr.isV4()) { isc_throw(BadValue, "Can't set siaddr to non-IPv4 address " - << siaddr.toText()); + << siaddr); } siaddr_ = siaddr; } @@ -263,9 +266,8 @@ Subnet::addPool(const PoolPtr& pool) { IOAddress last_addr = pool->getLastAddress(); if (!inRange(first_addr) || !inRange(last_addr)) { - isc_throw(BadValue, "Pool (" << first_addr.toText() << "-" - << last_addr.toText() - << " does not belong in this (" << prefix_.toText() << "/" + isc_throw(BadValue, "Pool (" << first_addr << "-" << last_addr + << " does not belong in this (" << prefix_ << "/" << static_cast<int>(prefix_len_) << ") subnet"); } @@ -332,7 +334,7 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, :Subnet(prefix, length, t1, t2, valid_lifetime), preferred_(preferred_lifetime){ if (!prefix.isV6()) { - isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText() + isc_throw(BadValue, "Non IPv6 prefix " << prefix << " specified in subnet6"); } } diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 31dc9477af..ecac6c3af8 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -366,6 +366,15 @@ public: /// @return textual representation virtual std::string toText() const; + /// @brief Resets subnet-id counter to its initial value (1) + /// + /// This should be called during reconfiguration, before any new + /// subnet objects are created. It will ensure that the subnet_id will + /// be consistent between reconfigures. + static void resetSubnetID() { + static_id_ = 1; + } + protected: /// @brief Returns all pools (non-const variant) /// @@ -378,7 +387,12 @@ protected: /// @brief Protected constructor // /// By making the constructor protected, we make sure that noone will - /// ever instantiate that class. Pool4 and Pool6 should be used instead. + /// ever instantiate that class. Subnet4 and Subnet6 should be used instead. + /// + /// This constructor assigns a new subnet-id (see @ref generateNextID). + /// This subnet-id has unique value that is strictly monotonously increasing + /// for each subnet, until it is explicitly reset back to 1 during + /// reconfiguration process. Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len, const Triplet<uint32_t>& t1, const Triplet<uint32_t>& t2, @@ -390,12 +404,24 @@ protected: /// derive from this class. virtual ~Subnet() { }; + /// @brief keeps the subnet-id value + /// + /// It is inreased every time a new Subnet object is created. + /// It is reset (@ref resetSubnetId) every time reconfiguration occurs. + /// + /// Static value initialized in subnet.cc. + static SubnetID static_id_; + /// @brief returns the next unique Subnet-ID /// + /// This method generates and returns the next unique subnet-id. + /// It is a strictly monotonously increasing value (1,2,3,...) for + /// each new Subnet object created. It can be explicitly reset + /// back to 1 during reconfiguration (@ref resetSubnetID). + /// /// @return the next unique Subnet-ID - static SubnetID getNextID() { - static SubnetID id = 0; - return (id++); + static SubnetID generateNextID() { + return (static_id_++); } /// @brief Checks if used pool type is valid @@ -495,6 +521,8 @@ public: /// @brief Constructor with all parameters /// + /// This constructor calls Subnet::Subnet, where subnet-id is generated. + /// /// @param prefix Subnet4 prefix /// @param length prefix length /// @param t1 renewal timer (in seconds) @@ -559,6 +587,8 @@ public: /// @brief Constructor with all parameters /// + /// This constructor calls Subnet::Subnet, where subnet-id is generated. + /// /// @param prefix Subnet6 prefix /// @param length prefix length /// @param t1 renewal timer (in seconds) diff --git a/src/lib/dhcpsrv/tests/.gitignore b/src/lib/dhcpsrv/tests/.gitignore index 7add7fb019..33ac8d9e86 100644 --- a/src/lib/dhcpsrv/tests/.gitignore +++ b/src/lib/dhcpsrv/tests/.gitignore @@ -1 +1,2 @@ /libdhcpsrv_unittests +/test_libraries.h diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am index 643fd635a2..28f804968c 100644 --- a/src/lib/dhcpsrv/tests/Makefile.am +++ b/src/lib/dhcpsrv/tests/Makefile.am @@ -33,7 +33,8 @@ if HAVE_GTEST # to unexpected errors. For this reason, the --enable-static-link option is # ignored for unit tests built here. -lib_LTLIBRARIES = libco1.la libco2.la +nodistdir=$(abs_top_builddir)/src/lib/dhcpsrv/tests +nodist_LTLIBRARIES = libco1.la libco2.la libco1_la_SOURCES = callout_library.cc libco1_la_CXXFLAGS = $(AM_CXXFLAGS) @@ -52,6 +53,7 @@ libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc +libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc libdhcpsrv_unittests_SOURCES += lease_unittest.cc libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc @@ -88,6 +90,7 @@ endif libdhcpsrv_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la +libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la diff --git a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc index fda8d59c31..4330efd423 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine_unittest.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 @@ -94,22 +94,56 @@ public: duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42))); iaid_ = 42; - // instantiate cfg_mgr + // Initialize a subnet and short address pool. + initSubnet(IOAddress("2001:db8:1::"), + IOAddress("2001:db8:1::10"), + IOAddress("2001:db8:1::20")); + + initFqdn("", false, false); + + factory_.create("type=memfile"); + } + + /// @brief Configures a subnet and adds one pool to it. + /// + /// This function removes existing v6 subnets before configuring + /// a new one. + /// + /// @param subnet Address of a subnet to be configured. + /// @param pool_start First address in the address pool. + /// @param pool_end Last address in the address pool. + void initSubnet(const IOAddress& subnet, const IOAddress& pool_start, + const IOAddress& pool_end) { CfgMgr& cfg_mgr = CfgMgr::instance(); + cfg_mgr.deleteSubnets6(); + + subnet_ = Subnet6Ptr(new Subnet6(subnet, 56, 1, 2, 3, 4)); + pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, pool_start, pool_end)); - // Configure normal address pool - subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4)); - pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::10"), - IOAddress("2001:db8:1::20"))); subnet_->addPool(pool_); - // Configure PD pool - pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 56, 64)); + pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, subnet, 56, 64)); subnet_->addPool(pd_pool_); cfg_mgr.addSubnet6(subnet_); - factory_.create("type=memfile"); + } + + /// @brief Initializes FQDN data for a test. + /// + /// The initialized values are used by the test fixture class members to + /// verify the correctness of a lease. + /// + /// @param hostname Hostname to be assigned to a lease. + /// @param fqdn_fwd Indicates whether or not to perform forward DNS update + /// for a lease. + /// @param fqdn_fwd Indicates whether or not to perform reverse DNS update + /// for a lease. + void initFqdn(const std::string& hostname, const bool fqdn_fwd, + const bool fqdn_rev) { + hostname_ = hostname; + fqdn_fwd_ = fqdn_fwd; + fqdn_rev_ = fqdn_rev; } /// @brief attempts to convert leases collection to a single lease @@ -151,10 +185,11 @@ public: EXPECT_EQ(subnet_->getT1(), lease->t1_); EXPECT_EQ(subnet_->getT2(), lease->t2_); EXPECT_EQ(exp_pd_len, lease->prefixlen_); - EXPECT_TRUE(false == lease->fqdn_fwd_); - EXPECT_TRUE(false == lease->fqdn_rev_); + EXPECT_EQ(fqdn_fwd_, lease->fqdn_fwd_); + EXPECT_EQ(fqdn_rev_, lease->fqdn_rev_); + EXPECT_EQ(hostname_, lease->hostname_); EXPECT_TRUE(*lease->duid_ == *duid_); - // @todo: check cltt + /// @todo: check cltt } /// @brief Checks if specified address is increased properly @@ -211,7 +246,7 @@ public: Lease6Ptr lease; EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, duid_, iaid_, hint, type, false, false, - "", fake, CalloutHandlePtr()))); + "", fake, CalloutHandlePtr(), old_leases_))); // Check that we got a lease EXPECT_TRUE(lease); @@ -275,16 +310,16 @@ public: Lease6Ptr lease; EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, duid_, iaid_, requested, type, false, false, "", false, - CalloutHandlePtr()))); + CalloutHandlePtr(), old_leases_))); // Check that we got a lease ASSERT_TRUE(lease); // Allocated address must be different - EXPECT_NE(used_addr.toText(), lease->addr_.toText()); + EXPECT_NE(used_addr, lease->addr_); // We should NOT get what we asked for, because it is used already - EXPECT_NE(requested.toText(), lease->addr_.toText()); + EXPECT_NE(requested, lease->addr_); // Do all checks on the lease checkLease6(lease, type, expected_pd_len); @@ -319,13 +354,13 @@ public: Lease6Ptr lease; EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, duid_, iaid_, hint, type, false, - false, "", false, CalloutHandlePtr()))); + false, "", false, CalloutHandlePtr(), old_leases_))); // Check that we got a lease ASSERT_TRUE(lease); // We should NOT get what we asked for, because it is used already - EXPECT_NE(hint.toText(), lease->addr_.toText()); + EXPECT_NE(hint, lease->addr_); // Do all checks on the lease checkLease6(lease, type, expected_pd_len); @@ -353,7 +388,14 @@ public: Subnet6Ptr subnet_; ///< subnet6 (used in tests) Pool6Ptr pool_; ///< NA pool belonging to subnet_ Pool6Ptr pd_pool_; ///< PD pool belonging to subnet_ + std::string hostname_; ///< Hostname + bool fqdn_fwd_; ///< Perform forward update for a lease. + bool fqdn_rev_; ///< Perform reverse update for a lease. LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory + + /// @brief Collection of leases being replaced by newly allocated or renewed + /// leases. + Lease6Collection old_leases_; }; /// @brief Used in Allocation Engine tests for IPv4 @@ -408,7 +450,7 @@ public: EXPECT_TRUE(*lease->client_id_ == *clientid_); } EXPECT_TRUE(lease->hwaddr_ == hwaddr_->hwaddr_); - // @todo: check cltt + /// @todo: check cltt } virtual ~AllocEngine4Test() { @@ -476,7 +518,7 @@ TEST_F(AllocEngine6Test, allocWithValidHint6) { false); // We should get what we asked for - EXPECT_EQ(lease->addr_.toText(), "2001:db8:1::15"); + EXPECT_EQ("2001:db8:1::15", lease->addr_.toText()); } // This test checks if the address allocation with a hint that is in range, @@ -521,13 +563,13 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) { Lease6Ptr lease; EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6( Subnet6Ptr(), duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, - false, false, "", false, CalloutHandlePtr()))); + false, false, "", false, CalloutHandlePtr(), old_leases_))); ASSERT_FALSE(lease); // Allocations without DUID are not allowed either EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, DuidPtr(), iaid_, IOAddress("::"), Lease::TYPE_NA, false, - false, "", false, CalloutHandlePtr()))); + false, "", false, CalloutHandlePtr(), old_leases_))); ASSERT_FALSE(lease); } @@ -765,19 +807,18 @@ TEST_F(AllocEngine6Test, smallPool6) { ASSERT_TRUE(engine); IOAddress addr("2001:db8:1::ad"); - CfgMgr& cfg_mgr = CfgMgr::instance(); - cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration - // Create configuration similar to other tests, but with a single address pool - subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4)); - pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address - subnet_->addPool(pool_); - cfg_mgr.addSubnet6(subnet_); + // Create a subnet with a pool that has one address. + initSubnet(IOAddress("2001:db8:1::"), addr, addr); + + // Initialize FQDN for a lease. + initFqdn("myhost.example.com", true, true); Lease6Ptr lease; EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, - duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false, - "", false, CalloutHandlePtr()))); + duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_, + fqdn_rev_, hostname_, false, CalloutHandlePtr(), + old_leases_))); // Check that we got that single lease ASSERT_TRUE(lease); @@ -794,6 +835,11 @@ TEST_F(AllocEngine6Test, smallPool6) { // Now check that the lease in LeaseMgr has the same parameters detailCompareLease(lease, from_mgr); + + // This is a new lease allocation. The old lease corresponding to a newly + // allocated lease should be NULL. + ASSERT_EQ(1, old_leases_.size()); + EXPECT_FALSE(old_leases_[0]); } // This test checks if all addresses in a pool are currently used, the attempt @@ -826,8 +872,9 @@ TEST_F(AllocEngine6Test, outOfAddresses6) { Lease6Ptr lease2; EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(subnet_, duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false, - "", false, CalloutHandlePtr()))); + "", false, CalloutHandlePtr(), old_leases_))); EXPECT_FALSE(lease2); + } // This test checks if an expired lease can be reused in SOLICIT (fake allocation) @@ -837,14 +884,12 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) { ASSERT_TRUE(engine); IOAddress addr("2001:db8:1::ad"); - CfgMgr& cfg_mgr = CfgMgr::instance(); - cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration - // Create configuration similar to other tests, but with a single address pool - subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4)); - pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address - subnet_->addPool(pool_); - cfg_mgr.addSubnet6(subnet_); + // Create one subnet with a pool holding one address. + initSubnet(IOAddress("2001:db8:1::"), addr, addr); + + // Initialize FQDN data for the lease. + initFqdn("myhost.example.com", true, true); // Just a different duid DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff))); @@ -860,11 +905,12 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) { // CASE 1: Asking for any address EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, - duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false, "", true, - CalloutHandlePtr()))); + duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_, + fqdn_rev_, hostname_, true, CalloutHandlePtr(), + old_leases_))); // Check that we got that single lease ASSERT_TRUE(lease); - EXPECT_EQ(addr.toText(), lease->addr_.toText()); + EXPECT_EQ(addr, lease->addr_); // Do all checks on the lease (if subnet-id, preferred/valid times are ok etc.) checkLease6(lease, Lease::TYPE_NA, 128); @@ -872,11 +918,11 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) { // CASE 2: Asking specifically for this address EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, duid_, iaid_, addr, Lease::TYPE_NA, false, false, "", - true, CalloutHandlePtr()))); + true, CalloutHandlePtr(), old_leases_))); // Check that we got that single lease ASSERT_TRUE(lease); - EXPECT_EQ(addr.toText(), lease->addr_.toText()); + EXPECT_EQ(addr, lease->addr_); } // This test checks if an expired lease can be reused in REQUEST (actual allocation) @@ -903,16 +949,32 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) { 501, 502, 503, 504, other_subnetid, 0)); lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago lease->valid_lft_ = 495; // Lease was valid for 495 seconds + lease->fqdn_fwd_ = true; + lease->fqdn_rev_ = true; + lease->hostname_ = "myhost.example.com."; ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); // A client comes along, asking specifically for this address EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, duid_, iaid_, addr, Lease::TYPE_NA, false, false, "", - false, CalloutHandlePtr()))); + false, CalloutHandlePtr(), old_leases_))); // Check that he got that single lease ASSERT_TRUE(lease); - EXPECT_EQ(addr.toText(), lease->addr_.toText()); + EXPECT_EQ(addr, lease->addr_); + // This reactivated lease should have updated FQDN data. + EXPECT_TRUE(lease->hostname_.empty()); + EXPECT_FALSE(lease->fqdn_fwd_); + EXPECT_FALSE(lease->fqdn_rev_); + + // Check that the old lease has been returned. + Lease6Ptr old_lease = expectOneLease(old_leases_); + // It should at least have the same IPv6 address. + EXPECT_EQ(lease->addr_, old_lease->addr_); + // Check that it carries not updated FQDN data. + EXPECT_EQ("myhost.example.com.", old_lease->hostname_); + EXPECT_TRUE(old_lease->fqdn_fwd_); + EXPECT_TRUE(old_lease->fqdn_rev_); // Check that the lease is indeed updated in LeaseMgr Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, @@ -1075,10 +1137,10 @@ TEST_F(AllocEngine4Test, allocWithUsedHint4) { ASSERT_TRUE(lease); // Allocated address must be different - EXPECT_TRUE(used->addr_.toText() != lease->addr_.toText()); + EXPECT_NE(used->addr_, lease->addr_); // We should NOT get what we asked for, because it is used already - EXPECT_TRUE(lease->addr_.toText() != "192.0.2.106"); + EXPECT_NE("192.0.2.106", lease->addr_.toText()); // Do all checks on the lease checkLease4(lease); @@ -1115,7 +1177,7 @@ TEST_F(AllocEngine4Test, allocBogusHint4) { EXPECT_FALSE(old_lease_); // We should NOT get what we asked for, because it is used already - EXPECT_TRUE(lease->addr_.toText() != "10.1.1.1"); + EXPECT_NE("10.1.1.1", lease->addr_.toText()); // Do all checks on the lease checkLease4(lease); @@ -1371,7 +1433,7 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) { old_lease_); // Check that we got that single lease ASSERT_TRUE(lease); - EXPECT_EQ(addr.toText(), lease->addr_.toText()); + EXPECT_EQ(addr, lease->addr_); // We are reusing expired lease, the old (expired) instance should be // returned. The returned instance should be the same as the original @@ -1384,13 +1446,13 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) { // CASE 2: Asking specifically for this address lease = engine->allocateLease4(subnet_, clientid_, hwaddr_, - IOAddress(addr.toText()), + IOAddress(addr), false, false, "", true, CalloutHandlePtr(), old_lease_); // Check that we got that single lease ASSERT_TRUE(lease); - EXPECT_EQ(addr.toText(), lease->addr_.toText()); + EXPECT_EQ(addr, lease->addr_); // We are updating expired lease. The copy of the old lease should be // returned and it should be equal to the original lease. @@ -1425,14 +1487,14 @@ TEST_F(AllocEngine4Test, requestReuseExpiredLease4) { // A client comes along, asking specifically for this address lease = engine->allocateLease4(subnet_, clientid_, hwaddr_, - IOAddress(addr.toText()), + IOAddress(addr), false, true, "host.example.com.", false, CalloutHandlePtr(), old_lease_); // Check that he got that single lease ASSERT_TRUE(lease); - EXPECT_EQ(addr.toText(), lease->addr_.toText()); + EXPECT_EQ(addr, lease->addr_); // Check that the lease is indeed updated in LeaseMgr Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr); @@ -1481,7 +1543,7 @@ TEST_F(AllocEngine4Test, renewLease4) { callout_handle, false); // Check that he got that single lease ASSERT_TRUE(lease); - EXPECT_EQ(addr.toText(), lease->addr_.toText()); + EXPECT_EQ(addr, lease->addr_); // Check that the lease matches subnet_, hwaddr_,clientid_ parameters checkLease4(lease); @@ -1623,7 +1685,7 @@ TEST_F(HookAllocEngine6Test, lease6_select) { Lease6Ptr lease; EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false, - "", false, callout_handle))); + "", false, callout_handle, old_leases_))); // Check that we got a lease ASSERT_TRUE(lease); @@ -1694,7 +1756,7 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) { Lease6Ptr lease; EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_, duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false, - "", false, callout_handle))); + "", false, callout_handle, old_leases_))); // Check that we got a lease ASSERT_TRUE(lease); diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc index 08ce768f62..e13cb320aa 100644 --- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.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 @@ -538,7 +538,7 @@ TEST_F(CfgMgrTest, optionSpace4) { cfg_mgr.addOptionSpace4(space3), isc::dhcp::InvalidOptionSpace ); - // @todo decode if a duplicate vendor space is allowed. + /// @todo decode if a duplicate vendor space is allowed. } // This test verifies that new DHCPv6 option spaces can be added to @@ -571,7 +571,7 @@ TEST_F(CfgMgrTest, optionSpace6) { cfg_mgr.addOptionSpace6(space3), isc::dhcp::InvalidOptionSpace ); - // @todo decide if a duplicate vendor space is allowed. + /// @todo decide if a duplicate vendor space is allowed. } // This test verifies that it is possible to specify interfaces that server @@ -670,6 +670,48 @@ TEST_F(CfgMgrTest, echoClientId) { EXPECT_TRUE(cfg_mgr.echoClientId()); } +// This test checks the D2ClientMgr wrapper methods. +TEST_F(CfgMgrTest, d2ClientConfig) { + // 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()); + + // Verify that we cannot set the configuration to an empty pointer. + D2ClientConfigPtr new_cfg; + ASSERT_THROW(CfgMgr::instance().setD2ClientConfig(new_cfg), D2ClientError); + + // Create a new, enabled configuration. + 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, + "pre-fix", "suf-fix"))); + + // Verify that we can assign a new, non-empty configuration. + ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(new_cfg)); + + // Verify that we can fetch the newly assigned configuration. + D2ClientConfigPtr updated_config = CfgMgr::instance().getD2ClientConfig(); + ASSERT_TRUE(updated_config); + EXPECT_TRUE(updated_config->getEnableUpdates()); + + // Make sure convenience method agrees with updated configuration. + EXPECT_TRUE(CfgMgr::instance().ddnsEnabled()); + + // Make sure the configuration we fetched is the one we assigned, + // and not the original configuration. + EXPECT_EQ(*new_cfg, *updated_config); + EXPECT_NE(*original_config, *updated_config); +} + + /// @todo Add unit-tests for testing: /// - addActiveIface() with invalid interface name /// - addActiveIface() with the same interface twice diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc new file mode 100644 index 0000000000..8a8550fbe2 --- /dev/null +++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc @@ -0,0 +1,798 @@ +// 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 +// 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 <config.h> +#include <dhcp/option4_client_fqdn.h> +#include <dhcp/option6_client_fqdn.h> +#include <dhcpsrv/d2_client.h> +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::util; +using namespace isc; + +namespace { + +/// @brief Checks constructors and accessors of D2ClientConfig. +TEST(D2ClientConfigTest, constructorsAndAccessors) { + D2ClientConfigPtr d2_client_config; + + // Verify default constructor creates a disabled instance. + ASSERT_NO_THROW(d2_client_config.reset(new D2ClientConfig())); + EXPECT_FALSE(d2_client_config->getEnableUpdates()); + + d2_client_config.reset(); + + bool enable_updates = true; + isc::asiolink::IOAddress server_ip("127.0.0.1"); + size_t server_port = 477; + dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP; + dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON; + bool always_include_fqdn = true; + bool override_no_update = true; + bool override_client_update = true; + bool replace_client_name = true; + std::string generated_prefix = "the_prefix"; + std::string qualifying_suffix = "the.suffix."; + + // Verify that we can construct a valid, enabled instance. + ASSERT_NO_THROW(d2_client_config.reset(new + D2ClientConfig(enable_updates, + server_ip, + server_port, + ncr_protocol, + ncr_format, + always_include_fqdn, + override_no_update, + override_client_update, + replace_client_name, + generated_prefix, + qualifying_suffix))); + + ASSERT_TRUE(d2_client_config); + + // Verify that the accessors return the expected values. + EXPECT_EQ(d2_client_config->getEnableUpdates(), enable_updates); + + EXPECT_EQ(d2_client_config->getServerIp(), server_ip); + 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->getAlwaysIncludeFqdn(), always_include_fqdn); + EXPECT_EQ(d2_client_config->getOverrideNoUpdate(), override_no_update); + EXPECT_EQ(d2_client_config->getOverrideClientUpdate(), + override_client_update); + EXPECT_EQ(d2_client_config->getReplaceClientName(), replace_client_name); + EXPECT_EQ(d2_client_config->getGeneratedPrefix(), generated_prefix); + EXPECT_EQ(d2_client_config->getQualifyingSuffix(), qualifying_suffix); + + // Verify that toText called by << operator doesn't bomb. + ASSERT_NO_THROW(std::cout << "toText test:" << std::endl << + *d2_client_config << std::endl); + + // Verify that constructor does not allow use of NCR_TCP. + /// @todo obviously this becomes invalid once TCP is supported. + ASSERT_THROW(d2_client_config.reset(new + D2ClientConfig(enable_updates, + server_ip, + server_port, + dhcp_ddns::NCR_TCP, + ncr_format, + always_include_fqdn, + override_no_update, + override_client_update, + replace_client_name, + generated_prefix, + qualifying_suffix)), + D2ClientError); + + /// @todo if additional validation is added to ctor, this test needs to + /// expand accordingly. +} + +/// @brief Tests the equality and inequality operators of D2ClientConfig. +TEST(D2ClientConfigTest, equalityOperator) { + D2ClientConfigPtr ref_config; + D2ClientConfigPtr test_config; + + isc::asiolink::IOAddress ref_address("127.0.0.1"); + isc::asiolink::IOAddress test_address("127.0.0.2"); + + // Create an instance to use as a reference. + ASSERT_NO_THROW(ref_config.reset(new D2ClientConfig(true, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + true, true, true, true, + "pre-fix", "suf-fix"))); + ASSERT_TRUE(ref_config); + + // Check a configuration that is identical to reference configuration. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + true, true, true, true, + "pre-fix", "suf-fix"))); + ASSERT_TRUE(test_config); + EXPECT_TRUE(*ref_config == *test_config); + EXPECT_FALSE(*ref_config != *test_config); + + // Check a configuration that differs only by enable flag. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(false, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + 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 server ip. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + test_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + 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 server port. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + ref_address, 333, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + 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 always_include_fqdn. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + false, 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 override_no_update. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + true, false, 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 override_client_update. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + true, true, false, 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 replace_client_name. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + true, true, true, false, + "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 generated_prefix. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + true, true, true, true, + "bogus", "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 qualifying_suffix. + ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true, + ref_address, 477, + dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON, + true, true, true, true, + "pre-fix", "bogus"))); + ASSERT_TRUE(test_config); + EXPECT_FALSE(*ref_config == *test_config); + EXPECT_TRUE(*ref_config != *test_config); +} + +/// @brief Checks the D2ClientMgr constructor. +TEST(D2ClientMgr, constructor) { + D2ClientMgrPtr d2_client_mgr; + + // Verify we can construct with the default constructor. + ASSERT_NO_THROW(d2_client_mgr.reset(new D2ClientMgr())); + + // After construction, D2 configuration should be disabled. + // Fetch it and verify this is the case. + D2ClientConfigPtr original_config = d2_client_mgr->getD2ClientConfig(); + ASSERT_TRUE(original_config); + EXPECT_FALSE(original_config->getEnableUpdates()); + + // Make sure convenience method agrees. + EXPECT_FALSE(d2_client_mgr->ddnsEnabled()); +} + +/// @brief Checks passing the D2ClientMgr a valid D2 client configuration. +/// @todo Once NameChangeSender is integrated, this test needs to expand, and +/// additional scenario tests will need to be written. +TEST(D2ClientMgr, validConfig) { + D2ClientMgrPtr d2_client_mgr; + + // Construct the manager and fetch its initial configuration. + ASSERT_NO_THROW(d2_client_mgr.reset(new D2ClientMgr())); + D2ClientConfigPtr original_config = d2_client_mgr->getD2ClientConfig(); + ASSERT_TRUE(original_config); + + // Verify that we cannot set the config to an empty pointer. + D2ClientConfigPtr new_cfg; + ASSERT_THROW(d2_client_mgr->setD2ClientConfig(new_cfg), D2ClientError); + + // Create a new, enabled config. + 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, + "pre-fix", "suf-fix"))); + + // Verify that we can assign a new, non-empty configuration. + ASSERT_NO_THROW(d2_client_mgr->setD2ClientConfig(new_cfg)); + + // Verify that we can fetch the newly assigned configuration. + D2ClientConfigPtr updated_config = d2_client_mgr->getD2ClientConfig(); + ASSERT_TRUE(updated_config); + EXPECT_TRUE(updated_config->getEnableUpdates()); + + // Make sure convenience method agrees with the updated configuration. + EXPECT_TRUE(d2_client_mgr->ddnsEnabled()); + + // Make sure the configuration we fetched is the one we assigned, + // and not the original configuration. + EXPECT_EQ(*new_cfg, *updated_config); + EXPECT_NE(*original_config, *updated_config); +} + + +/// @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 928be23ea3..78e3442273 100644 --- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc +++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.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 @@ -367,8 +367,8 @@ public: /// /// Note that the method currently it only supports option-defs, option-data /// and hooks-libraries. - /// - /// @param config_id is the name of the configuration element. + /// + /// @param config_id is the name of the configuration element. /// /// @return returns a shared pointer to DhcpConfigParser. /// @@ -376,20 +376,21 @@ public: ParserPtr createConfigParser(const std::string& config_id) { ParserPtr parser; if (config_id.compare("option-data") == 0) { - parser.reset(new OptionDataListParser(config_id, - parser_context_->options_, + parser.reset(new OptionDataListParser(config_id, + parser_context_->options_, parser_context_, UtestOptionDataParser::factory)); } else if (config_id.compare("option-def") == 0) { - parser.reset(new OptionDefListParser(config_id, + parser.reset(new OptionDefListParser(config_id, parser_context_->option_defs_)); } else if (config_id.compare("hooks-libraries") == 0) { parser.reset(new HooksLibrariesParser(config_id)); hooks_libraries_parser_ = boost::dynamic_pointer_cast<HooksLibrariesParser>(parser); - + } else if (config_id.compare("dhcp-ddns") == 0) { + parser.reset(new D2ClientConfigParser(config_id)); } else { isc_throw(NotImplemented, "Parser error: configuration parameter not supported: " @@ -399,8 +400,8 @@ public: return (parser); } - /// @brief Convenience method for parsing a configuration - /// + /// @brief Convenience method for parsing a configuration + /// /// Given a configuration string, convert it into Elements /// and parse them. /// @param config is the configuration string to parse @@ -491,6 +492,10 @@ public: // Ensure no hooks libraries are loaded. HooksManager::unloadLibraries(); + + // Set it to minimal, disabled config + D2ClientConfigPtr tmp(new D2ClientConfig()); + CfgMgr::instance().setD2ClientConfig(tmp); } /// @brief Parsers used in the parsing of the configuration @@ -566,7 +571,7 @@ TEST_F(ParseConfigTest, basicOptionDataTest) { " \"name\": \"foo\"," " \"space\": \"isc\"," " \"code\": 100," - " \"data\": \"192.168.2.1\"," + " \"data\": \"192.0.2.0\"," " \"csv-format\": True" " } ]" "}"; @@ -581,7 +586,7 @@ TEST_F(ParseConfigTest, basicOptionDataTest) { // Verify that the option definition is correct. std::string val = "type=100, len=4, data fields:\n " - " #0 192.168.2.1 ( ipv4-address ) \n"; + " #0 192.0.2.0 ( ipv4-address ) \n"; EXPECT_EQ(val, opt_ptr->toText()); } @@ -677,7 +682,7 @@ TEST_F(ParseConfigTest, validHooksLibrariesTest) { // Check with a set of libraries, some of which are invalid. TEST_F(ParseConfigTest, invalidHooksLibrariesTest) { - // @todo Initialize global library context to null + /// @todo Initialize global library context to null // Configuration string. This contains an invalid library which should // trigger an error in the "build" stage. @@ -703,6 +708,252 @@ TEST_F(ParseConfigTest, invalidHooksLibrariesTest) { "Error text returned from parse failure is " << error_text_; } +/// @brief Checks that a valid, enabled D2 client configuration works correctly. +TEST_F(ParseConfigTest, validD2Config) { + + // Configuration string containing valid values. + std::string config_str = + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : true, " + " \"server-ip\" : \"192.0.2.0\", " + " \"server-port\" : 3432, " + " \"ncr-protocol\" : \"UDP\", " + " \"ncr-format\" : \"JSON\", " + " \"always-include-fqdn\" : true, " + " \"override-no-update\" : true, " + " \"override-client-update\" : true, " + " \"replace-client-name\" : true, " + " \"generated-prefix\" : \"test.prefix\", " + " \"qualifying-suffix\" : \"test.suffix.\" " + " }" + "}"; + + // Verify that the configuration string parses. + int rcode = parseConfiguration(config_str); + ASSERT_TRUE(rcode == 0) << error_text_; + + // Verify that DHCP-DDNS is enabled and we can fetch the configuration. + EXPECT_TRUE(CfgMgr::instance().ddnsEnabled()); + D2ClientConfigPtr d2_client_config; + 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_EQ("192.0.2.0", d2_client_config->getServerIp().toText()); + 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->getAlwaysIncludeFqdn()); + EXPECT_TRUE(d2_client_config->getOverrideNoUpdate()); + EXPECT_TRUE(d2_client_config->getOverrideClientUpdate()); + EXPECT_TRUE(d2_client_config->getReplaceClientName()); + EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix()); + EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix()); + + // Another valid Configuration string. + // This one is disabled, has IPV6 server ip, control flags false, + // empty prefix/suffix + std::string config_str2 = + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : false, " + " \"server-ip\" : \"2001:db8::\", " + " \"server-port\" : 43567, " + " \"ncr-protocol\" : \"UDP\", " + " \"ncr-format\" : \"JSON\", " + " \"always-include-fqdn\" : false, " + " \"override-no-update\" : false, " + " \"override-client-update\" : false, " + " \"replace-client-name\" : false, " + " \"generated-prefix\" : \"\", " + " \"qualifying-suffix\" : \"\" " + " }" + "}"; + + // Verify that the configuration string parses. + rcode = parseConfiguration(config_str2); + ASSERT_TRUE(rcode == 0) << error_text_; + + // 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_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->getAlwaysIncludeFqdn()); + EXPECT_FALSE(d2_client_config->getOverrideNoUpdate()); + EXPECT_FALSE(d2_client_config->getOverrideClientUpdate()); + EXPECT_FALSE(d2_client_config->getReplaceClientName()); + EXPECT_EQ("", d2_client_config->getGeneratedPrefix()); + EXPECT_EQ("", d2_client_config->getQualifyingSuffix()); +} + +/// @brief Checks that D2 client can be configured with enable flag of +/// false only. +TEST_F(ParseConfigTest, validDisabledD2Config) { + + // Configuration string. This contains a set of valid libraries. + std::string config_str = + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : false" + " }" + "}"; + + // Verify that the configuration string parses. + int rcode = parseConfiguration(config_str); + ASSERT_TRUE(rcode == 0) << error_text_; + + // Verify that DHCP-DDNS is disabled. + EXPECT_FALSE(CfgMgr::instance().ddnsEnabled()); + + // Make sure fetched config agrees. + D2ClientConfigPtr d2_client_config; + ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig()); + EXPECT_TRUE(d2_client_config); + EXPECT_FALSE(d2_client_config->getEnableUpdates()); +} + +/// @brief Check various invalid D2 client configurations. +TEST_F(ParseConfigTest, invalidD2Config) { + std::string invalid_configs[] = { + // only the enable flag of true + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : true" + " }" + "}", + // Missing server ip value + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : true, " + //" \"server-ip\" : \"192.0.2.0\", " + " \"server-port\" : 53001, " + " \"ncr-protocol\" : \"UDP\", " + " \"ncr-format\" : \"JSON\", " + " \"always-include-fqdn\" : true, " + " \"override-no-update\" : true, " + " \"override-client-update\" : true, " + " \"replace-client-name\" : true, " + " \"generated-prefix\" : \"test.prefix\", " + " \"qualifying-suffix\" : \"test.suffix.\" " + " }" + "}", + // Invalid server ip value + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : true, " + " \"server-ip\" : \"x192.0.2.0\", " + " \"server-port\" : 53001, " + " \"ncr-protocol\" : \"UDP\", " + " \"ncr-format\" : \"JSON\", " + " \"always-include-fqdn\" : true, " + " \"override-no-update\" : true, " + " \"override-client-update\" : true, " + " \"replace-client-name\" : true, " + " \"generated-prefix\" : \"test.prefix\", " + " \"qualifying-suffix\" : \"test.suffix.\" " + " }" + "}", + // Unknown protocol + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : true, " + " \"server-ip\" : \"192.0.2.0\", " + " \"server-port\" : 53001, " + " \"ncr-protocol\" : \"Bogus\", " + " \"ncr-format\" : \"JSON\", " + " \"always-include-fqdn\" : true, " + " \"override-no-update\" : true, " + " \"override-client-update\" : true, " + " \"replace-client-name\" : true, " + " \"generated-prefix\" : \"test.prefix\", " + " \"qualifying-suffix\" : \"test.suffix.\" " + " }" + "}", + // Unsupported protocol + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : true, " + " \"server-ip\" : \"192.0.2.0\", " + " \"server-port\" : 53001, " + " \"ncr-protocol\" : \"TCP\", " + " \"ncr-format\" : \"JSON\", " + " \"always-include-fqdn\" : true, " + " \"override-no-update\" : true, " + " \"override-client-update\" : true, " + " \"replace-client-name\" : true, " + " \"generated-prefix\" : \"test.prefix\", " + " \"qualifying-suffix\" : \"test.suffix.\" " + " }" + "}", + // Unknown format + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : true, " + " \"server-ip\" : \"192.0.2.0\", " + " \"server-port\" : 53001, " + " \"ncr-protocol\" : \"UDP\", " + " \"ncr-format\" : \"Bogus\", " + " \"always-include-fqdn\" : true, " + " \"override-no-update\" : true, " + " \"override-client-update\" : true, " + " \"replace-client-name\" : true, " + " \"generated-prefix\" : \"test.prefix\", " + " \"qualifying-suffix\" : \"test.suffix.\" " + " }" + "}", + // Missig Port + "{ \"dhcp-ddns\" :" + " {" + " \"enable-updates\" : true, " + " \"server-ip\" : \"192.0.2.0\", " + // " \"server-port\" : 53001, " + " \"ncr-protocol\" : \"UDP\", " + " \"ncr-format\" : \"JSON\", " + " \"always-include-fqdn\" : true, " + " \"override-no-update\" : true, " + " \"override-client-update\" : true, " + " \"replace-client-name\" : true, " + " \"generated-prefix\" : \"test.prefix\", " + " \"qualifying-suffix\" : \"test.suffix.\" " + " }" + "}", + // stop + "" + }; + + // Fetch the original config. + D2ClientConfigPtr original_config; + ASSERT_NO_THROW(original_config = CfgMgr::instance().getD2ClientConfig()); + + // Iterate through the invalid configuration strings, attempting to + // parse each one. They should fail to parse, but fail gracefully. + D2ClientConfigPtr current_config; + int i = 0; + while (!invalid_configs[i].empty()) { + // Verify that the configuration string parses without throwing. + int rcode = parseConfiguration(invalid_configs[i]); + + // Verify that parse result indicates a parsing error. + ASSERT_TRUE(rcode != 0) << "Invalid config #: " << i + << " should not have passed!"; + + // Verify that the "official" config still matches the original config. + ASSERT_NO_THROW(current_config = + CfgMgr::instance().getD2ClientConfig()); + EXPECT_EQ(*original_config, *current_config); + ++i; + } +} + /// @brief DHCP Configuration Parser Context test fixture. class ParserContextTest : public ::testing::Test { public: diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index ab5a8b2162..f8a4aed3b5 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.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 @@ -251,17 +251,6 @@ public: namespace { -/// Hardware address used by different tests. -const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; -/// Client id used by different tests. -const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; -/// Valid lifetime value used by different tests. -const uint32_t VALID_LIFETIME = 500; -/// Subnet ID used by different tests. -const uint32_t SUBNET_ID = 42; -/// IAID value used by different tests. -const uint32_t IAID = 7; - /// @brief getParameter test /// /// This test checks if the LeaseMgr can be instantiated and that it @@ -320,616 +309,6 @@ TEST_F(LeaseMgrTest, getLease6) { // are purely virtual, so we would only call ConcreteLeaseMgr methods. // Those methods are just stubs that do not return anything. -/// @brief Lease4 Constructor Test -/// -/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well. -// This test checks if the Lease4 structure can be instantiated correctly -TEST(Lease4, constructor) { - - // Random values for the tests - const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; - std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); - - const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; - std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); - ClientId clientid(clientid_vec); - - // ...and a time - const time_t current_time = time(NULL); - - // Other random constants. - const uint32_t SUBNET_ID = 42; - const uint32_t VALID_LIFETIME = 500; - - // We want to check that various addresses work, so let's iterate over - // these. - const uint32_t ADDRESS[] = { - 0x00000000, 0x01020304, 0x7fffffff, 0x80000000, 0x80000001, 0xffffffff - }; - - for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) { - - // Create the lease - Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR), - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, - current_time, SUBNET_ID, true, true, - "hostname.example.com."); - - EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_)); - EXPECT_EQ(0, lease.ext_); - EXPECT_TRUE(hwaddr == lease.hwaddr_); - EXPECT_TRUE(clientid == *lease.client_id_); - EXPECT_EQ(0, lease.t1_); - EXPECT_EQ(0, lease.t2_); - EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_); - EXPECT_EQ(current_time, lease.cltt_); - EXPECT_EQ(SUBNET_ID, lease.subnet_id_); - EXPECT_FALSE(lease.fixed_); - EXPECT_EQ("hostname.example.com.", lease.hostname_); - EXPECT_TRUE(lease.fqdn_fwd_); - EXPECT_TRUE(lease.fqdn_rev_); - EXPECT_TRUE(lease.comments_.empty()); - } -} - -// This test verfies that copy constructor copies Lease4 fields correctly. -TEST(Lease4, copyConstructor) { - - // Random values for the tests - const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; - std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); - - const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; - std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); - ClientId clientid(clientid_vec); - - // ...and a time - const time_t current_time = time(NULL); - - // Other random constants. - const uint32_t SUBNET_ID = 42; - const uint32_t VALID_LIFETIME = 500; - - // Create the lease - Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR), - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time, - SUBNET_ID); - - // Use copy constructor to copy the lease. - Lease4 copied_lease(lease); - - // Both leases should be now equal. When doing this check we assume that - // the equality operator works correctly. - EXPECT_TRUE(lease == copied_lease); - // Client IDs are equal, but they should be in two distinct pointers. - EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_); -} - -// This test verfies that the assignment operator copies all Lease4 fields -// correctly. -TEST(Lease4, operatorAssign) { - - // Random values for the tests - const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; - std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); - - const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; - std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); - ClientId clientid(clientid_vec); - - // ...and a time - const time_t current_time = time(NULL); - - // Other random constants. - const uint32_t SUBNET_ID = 42; - const uint32_t VALID_LIFETIME = 500; - - // Create the lease - Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR), - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time, - SUBNET_ID); - - // Use assignment operator to assign the lease. - Lease4 copied_lease = lease; - - // Both leases should be now equal. When doing this check we assume that - // the equality operator works correctly. - EXPECT_TRUE(lease == copied_lease); - // Client IDs are equal, but they should be in two distinct pointers. - EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_); -} - -// This test verifies that the matches() returns true if two leases differ -// by values other than address, HW address, Client ID and ext_. -TEST(Lease4, matches) { - // Create two leases which share the same address, HW address, client id - // and ext_ value. - const time_t current_time = time(NULL); - Lease4 lease1(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID, - sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0, - SUBNET_ID); - lease1.hostname_ = "lease1.example.com."; - lease1.fqdn_fwd_ = true; - lease1.fqdn_rev_ = true; - Lease4 lease2(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID, - sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10, - 100, 200, SUBNET_ID); - lease2.hostname_ = "lease2.example.com."; - lease2.fqdn_fwd_ = false; - lease2.fqdn_rev_ = true; - - // Leases should match. - EXPECT_TRUE(lease1.matches(lease2)); - EXPECT_TRUE(lease2.matches(lease1)); - - // Change address, leases should not match anymore. - lease1.addr_ = IOAddress("192.0.2.4"); - EXPECT_FALSE(lease1.matches(lease2)); - lease1.addr_ = lease2.addr_; - - // Change HW address, leases should not match. - lease1.hwaddr_[1] += 1; - EXPECT_FALSE(lease1.matches(lease2)); - lease1.hwaddr_ = lease2.hwaddr_; - - // Chanage client id, leases should not match. - std::vector<uint8_t> client_id = lease1.client_id_->getClientId(); - client_id[1] += 1; - lease1.client_id_.reset(new ClientId(client_id)); - EXPECT_FALSE(lease1.matches(lease2)); - lease1.client_id_ = lease2.client_id_; - - // Change ext_, leases should not match. - lease1.ext_ += 1; - EXPECT_FALSE(lease1.matches(lease2)); - lease1.ext_ = lease2.ext_; -} - -/// @brief Lease4 Equality Test -/// -/// Checks that the operator==() correctly compares two leases for equality. -/// As operator!=() is also defined for this class, every check on operator==() -/// is followed by the reverse check on operator!=(). -TEST(Lease4, operatorEquals) { - - // Random values for the tests - const uint32_t ADDRESS = 0x01020304; - const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; - std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); - const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; - std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); - ClientId clientid(clientid_vec); - const time_t current_time = time(NULL); - const uint32_t SUBNET_ID = 42; - const uint32_t VALID_LIFETIME = 500; - - // Check when the leases are equal. - Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR), - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, - 0, SUBNET_ID); - Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR), - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0, - SUBNET_ID); - EXPECT_TRUE(lease1 == lease2); - EXPECT_FALSE(lease1 != lease2); - - // Now vary individual fields in a lease and check that the leases compare - // not equal in every case. - lease1.addr_ = IOAddress(ADDRESS + 1); - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.addr_ = lease2.addr_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.ext_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.ext_ = lease2.ext_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.hwaddr_[0]; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.hwaddr_ = lease2.hwaddr_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++clientid_vec[0]; - lease1.client_id_.reset(new ClientId(clientid_vec)); - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - --clientid_vec[0]; - lease1.client_id_.reset(new ClientId(clientid_vec)); - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.t1_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.t1_ = lease2.t1_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.t2_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.t2_ = lease2.t2_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.valid_lft_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.valid_lft_ = lease2.valid_lft_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.cltt_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.cltt_ = lease2.cltt_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.subnet_id_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.subnet_id_ = lease2.subnet_id_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.fixed_ = !lease1.fixed_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.fixed_ = lease2.fixed_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.hostname_ += string("Something random"); - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.hostname_ = lease2.hostname_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.fqdn_fwd_ = !lease1.fqdn_fwd_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.fqdn_fwd_ = lease2.fqdn_fwd_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.fqdn_rev_ = !lease1.fqdn_rev_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.fqdn_rev_ = lease2.fqdn_rev_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.comments_ += string("Something random"); - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.comments_ = lease2.comments_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal -} - - - -// Lease6 is also defined in lease_mgr.h, so is tested in this file as well. -// This test checks if the Lease6 structure can be instantiated correctly -TEST(Lease6, Lease6ConstructorDefault) { - - // check a variety of addresses with different bits set. - const char* ADDRESS[] = { - "::", "::1", "2001:db8:1::456", - "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - "8000::", "8000::1", - "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" - }; - - // Other values - uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - DuidPtr duid(new DUID(llt, sizeof(llt))); - uint32_t iaid = 7; // Just a number - SubnetID subnet_id = 8; // Just another number - - for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) { - IOAddress addr(ADDRESS[i]); - Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, - duid, iaid, 100, 200, 50, 80, - subnet_id)); - - EXPECT_TRUE(lease->addr_ == addr); - EXPECT_TRUE(*lease->duid_ == *duid); - EXPECT_TRUE(lease->iaid_ == iaid); - EXPECT_TRUE(lease->subnet_id_ == subnet_id); - EXPECT_TRUE(lease->type_ == Lease::TYPE_NA); - EXPECT_TRUE(lease->preferred_lft_ == 100); - EXPECT_TRUE(lease->valid_lft_ == 200); - EXPECT_TRUE(lease->t1_ == 50); - EXPECT_TRUE(lease->t2_ == 80); - EXPECT_FALSE(lease->fqdn_fwd_); - EXPECT_FALSE(lease->fqdn_rev_); - EXPECT_TRUE(lease->hostname_.empty()); - - } - - // Lease6 must be instantiated with a DUID, not with NULL pointer - IOAddress addr(ADDRESS[0]); - Lease6Ptr lease2; - EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr, - DuidPtr(), iaid, 100, 200, 50, 80, - subnet_id)), InvalidOperation); -} - -// This test verifies that the Lease6 constructor which accepts FQDN data, -// sets the data correctly for the lease. -TEST(Lease6, Lease6ConstructorWithFQDN) { - - // check a variety of addresses with different bits set. - const char* ADDRESS[] = { - "::", "::1", "2001:db8:1::456", - "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - "8000::", "8000::1", - "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" - }; - - // Other values - uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - DuidPtr duid(new DUID(llt, sizeof(llt))); - uint32_t iaid = 7; // Just a number - SubnetID subnet_id = 8; // Just another number - - for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) { - IOAddress addr(ADDRESS[i]); - Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, - duid, iaid, 100, 200, 50, 80, subnet_id, - true, true, "host.example.com.")); - - EXPECT_TRUE(lease->addr_ == addr); - EXPECT_TRUE(*lease->duid_ == *duid); - EXPECT_TRUE(lease->iaid_ == iaid); - EXPECT_TRUE(lease->subnet_id_ == subnet_id); - EXPECT_TRUE(lease->type_ == Lease::TYPE_NA); - EXPECT_TRUE(lease->preferred_lft_ == 100); - EXPECT_TRUE(lease->valid_lft_ == 200); - EXPECT_TRUE(lease->t1_ == 50); - EXPECT_TRUE(lease->t2_ == 80); - EXPECT_TRUE(lease->fqdn_fwd_); - EXPECT_TRUE(lease->fqdn_rev_); - EXPECT_EQ("host.example.com.", lease->hostname_); - } - // Lease6 must be instantiated with a DUID, not with NULL pointer - IOAddress addr(ADDRESS[0]); - Lease6Ptr lease2; - EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr, - DuidPtr(), iaid, 100, 200, 50, 80, - subnet_id)), InvalidOperation); -} - -// This test verifies that the matches() function returns true if two leases -// differ by values other than address, type, prefix length, IAID and DUID. -TEST(Lease6, matches) { - - // Create two matching leases. - uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - DuidPtr duid(new DUID(llt, sizeof(llt))); - - Lease6 lease1(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid, - IAID, 100, 200, 50, 80, - SUBNET_ID); - lease1.hostname_ = "lease1.example.com."; - lease1.fqdn_fwd_ = true; - lease1.fqdn_rev_ = true; - Lease6 lease2(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid, - IAID, 200, 300, 90, 70, - SUBNET_ID); - lease2.hostname_ = "lease1.example.com."; - lease2.fqdn_fwd_ = false; - lease2.fqdn_rev_ = true; - - EXPECT_TRUE(lease1.matches(lease2)); - - // Modify each value used to match both leases, and make sure that - // leases don't match. - - // Modify address. - lease1.addr_ = IOAddress("2001:db8:1::2"); - EXPECT_FALSE(lease1.matches(lease2)); - lease1.addr_ = lease2.addr_; - - // Modify lease type. - lease1.type_ = Lease6::TYPE_TA; - EXPECT_FALSE(lease1.matches(lease2)); - lease1.type_ = lease2.type_; - - // Modify prefix length. - lease1.prefixlen_ += 1; - EXPECT_FALSE(lease1.matches(lease2)); - lease1.prefixlen_ = lease2.prefixlen_; - - // Modify IAID. - lease1.iaid_ += 1; - EXPECT_FALSE(lease1.matches(lease2)); - lease1.iaid_ = lease2.iaid_; - - // Modify DUID. - llt[1] += 1; - duid.reset(new DUID(llt, sizeof(llt))); - lease1.duid_ = duid; - EXPECT_FALSE(lease1.matches(lease2)); - lease1.duid_ = lease2.duid_; -} - -/// @brief Lease6 Equality Test -/// -/// Checks that the operator==() correctly compares two leases for equality. -/// As operator!=() is also defined for this class, every check on operator==() -/// is followed by the reverse check on operator!=(). -TEST(Lease6, OperatorEquals) { - - // check a variety of addresses with different bits set. - const IOAddress addr("2001:db8:1::456"); - uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - DuidPtr duid(new DUID(duid_array, sizeof(duid_array))); - uint32_t iaid = 7; // just a number - SubnetID subnet_id = 8; // just another number - - // Check for equality. - Lease6 lease1(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80, - subnet_id); - Lease6 lease2(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80, - subnet_id); - - // cltt_ constructs with time(NULL), make sure they are always equal - lease1.cltt_ = lease2.cltt_; - - EXPECT_TRUE(lease1 == lease2); - EXPECT_FALSE(lease1 != lease2); - - // Go through and alter all the fields one by one - - lease1.addr_ = IOAddress("::1"); - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.addr_ = lease2.addr_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.type_ = Lease::TYPE_PD; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.type_ = lease2.type_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.prefixlen_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.prefixlen_ = lease2.prefixlen_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.iaid_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.iaid_ = lease2.iaid_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++duid_array[0]; - lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array))); - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - --duid_array[0]; - lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array))); - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.preferred_lft_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.preferred_lft_ = lease2.preferred_lft_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.valid_lft_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.valid_lft_ = lease2.valid_lft_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.t1_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.t1_ = lease2.t1_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.t2_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.t2_ = lease2.t2_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.cltt_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.cltt_ = lease2.cltt_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - ++lease1.subnet_id_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.subnet_id_ = lease2.subnet_id_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.fixed_ = !lease1.fixed_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.fixed_ = lease2.fixed_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.hostname_ += string("Something random"); - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.hostname_ = lease2.hostname_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.fqdn_fwd_ = !lease1.fqdn_fwd_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.fqdn_fwd_ = lease2.fqdn_fwd_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.fqdn_rev_ = !lease1.fqdn_rev_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.fqdn_rev_ = lease2.fqdn_rev_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - - lease1.comments_ += string("Something random"); - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.comments_ = lease2.comments_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal -} - -// Checks if lease expiration is calculated properly -TEST(Lease6, Lease6Expired) { - const IOAddress addr("2001:db8:1::456"); - const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - const DuidPtr duid(new DUID(duid_array, sizeof(duid_array))); - const uint32_t iaid = 7; // Just a number - const SubnetID subnet_id = 8; // Just another number - Lease6 lease(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80, - subnet_id); - - // Case 1: a second before expiration - lease.cltt_ = time(NULL) - 100; - lease.valid_lft_ = 101; - EXPECT_FALSE(lease.expired()); - - // Case 2: the lease will expire after this second is concluded - lease.cltt_ = time(NULL) - 101; - EXPECT_FALSE(lease.expired()); - - // Case 3: the lease is expired - lease.cltt_ = time(NULL) - 102; - EXPECT_TRUE(lease.expired()); -} }; // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc index 6f57f30e39..4e7451f2d1 100644 --- a/src/lib/dhcpsrv/tests/lease_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_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 @@ -13,23 +13,342 @@ // PERFORMANCE OF THIS SOFTWARE. #include <config.h> +#include <asiolink/io_address.h> #include <dhcp/duid.h> #include <dhcpsrv/lease.h> #include <gtest/gtest.h> #include <vector> using namespace isc; +using namespace isc::asiolink; using namespace isc::dhcp; namespace { -// @todo Currently this file contains tests for new functions which return DUID -// or client identifier. Other tests for Lease objects must be implemented. -// See http://bind10.isc.org/ticket/3240. +/// Hardware address used by different tests. +const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; +/// Client id used by different tests. +const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; +/// Valid lifetime value used by different tests. +const uint32_t VALID_LIFETIME = 500; +/// Subnet ID used by different tests. +const uint32_t SUBNET_ID = 42; +/// IAID value used by different tests. +const uint32_t IAID = 7; + +/// @brief Creates an instance of the lease with certain FQDN data. +/// +/// @param hostname Hostname. +/// @param fqdn_fwd Forward FQDN update setting for a created lease. +/// @param fqdn_rev Reverse FQDN update setting for a created lease. +/// +/// @return Instance of the created lease. +Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd, + const bool fqdn_rev) { + Lease4 lease; + lease.hostname_ = hostname; + lease.fqdn_fwd_ = fqdn_fwd; + lease.fqdn_rev_ = fqdn_rev; + return (lease); +} + +/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well. +// This test checks if the Lease4 structure can be instantiated correctly +TEST(Lease4, constructor) { + + // Random values for the tests + const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; + std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); + + const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; + std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); + ClientId clientid(clientid_vec); + + // ...and a time + const time_t current_time = time(NULL); + + // Other random constants. + const uint32_t SUBNET_ID = 42; + const uint32_t VALID_LIFETIME = 500; + + // We want to check that various addresses work, so let's iterate over + // these. + const uint32_t ADDRESS[] = { + 0x00000000, 0x01020304, 0x7fffffff, 0x80000000, 0x80000001, 0xffffffff + }; + + for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) { + + // Create the lease + Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR), + CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, + current_time, SUBNET_ID, true, true, + "hostname.example.com."); + + EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_)); + EXPECT_EQ(0, lease.ext_); + EXPECT_TRUE(hwaddr == lease.hwaddr_); + EXPECT_TRUE(clientid == *lease.client_id_); + EXPECT_EQ(0, lease.t1_); + EXPECT_EQ(0, lease.t2_); + EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_); + EXPECT_EQ(current_time, lease.cltt_); + EXPECT_EQ(SUBNET_ID, lease.subnet_id_); + EXPECT_FALSE(lease.fixed_); + EXPECT_EQ("hostname.example.com.", lease.hostname_); + EXPECT_TRUE(lease.fqdn_fwd_); + EXPECT_TRUE(lease.fqdn_rev_); + EXPECT_TRUE(lease.comments_.empty()); + } +} + +// This test verfies that copy constructor copies Lease4 fields correctly. +TEST(Lease4, copyConstructor) { + + // Random values for the tests + const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; + std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); + + const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; + std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); + ClientId clientid(clientid_vec); + + // ...and a time + const time_t current_time = time(NULL); + + // Other random constants. + const uint32_t SUBNET_ID = 42; + const uint32_t VALID_LIFETIME = 500; + + // Create the lease + Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR), + CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time, + SUBNET_ID); + + // Use copy constructor to copy the lease. + Lease4 copied_lease(lease); + + // Both leases should be now equal. When doing this check we assume that + // the equality operator works correctly. + EXPECT_TRUE(lease == copied_lease); + // Client IDs are equal, but they should be in two distinct pointers. + EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_); +} + +// This test verfies that the assignment operator copies all Lease4 fields +// correctly. +TEST(Lease4, operatorAssign) { + + // Random values for the tests + const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; + std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); + + const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; + std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); + ClientId clientid(clientid_vec); + + // ...and a time + const time_t current_time = time(NULL); + + // Other random constants. + const uint32_t SUBNET_ID = 42; + const uint32_t VALID_LIFETIME = 500; + + // Create the lease + Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR), + CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time, + SUBNET_ID); + + // Use assignment operator to assign the lease. + Lease4 copied_lease = lease; + + // Both leases should be now equal. When doing this check we assume that + // the equality operator works correctly. + EXPECT_TRUE(lease == copied_lease); + // Client IDs are equal, but they should be in two distinct pointers. + EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_); +} + +// This test verifies that the matches() returns true if two leases differ +// by values other than address, HW address, Client ID and ext_. +TEST(Lease4, matches) { + // Create two leases which share the same address, HW address, client id + // and ext_ value. + const time_t current_time = time(NULL); + Lease4 lease1(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID, + sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0, + SUBNET_ID); + lease1.hostname_ = "lease1.example.com."; + lease1.fqdn_fwd_ = true; + lease1.fqdn_rev_ = true; + Lease4 lease2(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID, + sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10, + 100, 200, SUBNET_ID); + lease2.hostname_ = "lease2.example.com."; + lease2.fqdn_fwd_ = false; + lease2.fqdn_rev_ = true; + + // Leases should match. + EXPECT_TRUE(lease1.matches(lease2)); + EXPECT_TRUE(lease2.matches(lease1)); + + // Change address, leases should not match anymore. + lease1.addr_ = IOAddress("192.0.2.4"); + EXPECT_FALSE(lease1.matches(lease2)); + lease1.addr_ = lease2.addr_; + + // Change HW address, leases should not match. + lease1.hwaddr_[1] += 1; + EXPECT_FALSE(lease1.matches(lease2)); + lease1.hwaddr_ = lease2.hwaddr_; + + // Chanage client id, leases should not match. + std::vector<uint8_t> client_id = lease1.client_id_->getClientId(); + client_id[1] += 1; + lease1.client_id_.reset(new ClientId(client_id)); + EXPECT_FALSE(lease1.matches(lease2)); + lease1.client_id_ = lease2.client_id_; + + // Change ext_, leases should not match. + lease1.ext_ += 1; + EXPECT_FALSE(lease1.matches(lease2)); + lease1.ext_ = lease2.ext_; +} + +/// @brief Lease4 Equality Test +/// +/// Checks that the operator==() correctly compares two leases for equality. +/// As operator!=() is also defined for this class, every check on operator==() +/// is followed by the reverse check on operator!=(). +TEST(Lease4, operatorEquals) { + + // Random values for the tests + const uint32_t ADDRESS = 0x01020304; + const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; + std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); + const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; + std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); + ClientId clientid(clientid_vec); + const time_t current_time = time(NULL); + const uint32_t SUBNET_ID = 42; + const uint32_t VALID_LIFETIME = 500; + + // Check when the leases are equal. + Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR), + CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, + 0, SUBNET_ID); + Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR), + CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0, + SUBNET_ID); + EXPECT_TRUE(lease1 == lease2); + EXPECT_FALSE(lease1 != lease2); + + // Now vary individual fields in a lease and check that the leases compare + // not equal in every case. + lease1.addr_ = IOAddress(ADDRESS + 1); + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.addr_ = lease2.addr_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.ext_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.ext_ = lease2.ext_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.hwaddr_[0]; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.hwaddr_ = lease2.hwaddr_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++clientid_vec[0]; + lease1.client_id_.reset(new ClientId(clientid_vec)); + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + --clientid_vec[0]; + lease1.client_id_.reset(new ClientId(clientid_vec)); + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.t1_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.t1_ = lease2.t1_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.t2_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.t2_ = lease2.t2_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.valid_lft_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.valid_lft_ = lease2.valid_lft_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.cltt_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.cltt_ = lease2.cltt_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.subnet_id_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.subnet_id_ = lease2.subnet_id_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.fixed_ = !lease1.fixed_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.fixed_ = lease2.fixed_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.hostname_ += std::string("Something random"); + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.hostname_ = lease2.hostname_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.fqdn_fwd_ = !lease1.fqdn_fwd_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.fqdn_fwd_ = lease2.fqdn_fwd_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.fqdn_rev_ = !lease1.fqdn_rev_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.fqdn_rev_ = lease2.fqdn_rev_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.comments_ += std::string("Something random"); + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.comments_ = lease2.comments_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal +} // Verify that the client id can be returned as a vector object and if client // id is NULL the empty vector is returned. -TEST(Lease4Test, getClientIdVector) { +TEST(Lease4, getClientIdVector) { // Create a lease. Lease4 lease; // By default, the lease should have client id set to NULL. If it doesn't, @@ -47,9 +366,356 @@ TEST(Lease4Test, getClientIdVector) { EXPECT_TRUE(returned_vec == client_id_vec); } +// Verify the behavior of the function which checks FQDN data for equality. +TEST(Lease4, hasIdenticalFqdn) { + Lease4 lease = createLease4("myhost.example.com.", true, true); + EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.", + true, true))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("other.example.com.", + true, true))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.", + false, true))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.", + true, false))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.", + false, false))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("other.example.com.", + false, false))); +} + +/// @brief Creates an instance of the lease with certain FQDN data. +/// +/// @param hostname Hostname. +/// @param fqdn_fwd Forward FQDN update setting for a created lease. +/// @param fqdn_rev Reverse FQDN update setting for a created lease. +/// +/// @return Instance of the created lease. +Lease6 createLease6(const std::string& hostname, const bool fqdn_fwd, + const bool fqdn_rev) { + Lease6 lease; + lease.hostname_ = hostname; + lease.fqdn_fwd_ = fqdn_fwd; + lease.fqdn_rev_ = fqdn_rev; + return (lease); +} + +// Lease6 is also defined in lease_mgr.h, so is tested in this file as well. +// This test checks if the Lease6 structure can be instantiated correctly +TEST(Lease6, Lease6ConstructorDefault) { + + // check a variety of addresses with different bits set. + const char* ADDRESS[] = { + "::", "::1", "2001:db8:1::456", + "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "8000::", "8000::1", + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + }; + + // Other values + uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + DuidPtr duid(new DUID(llt, sizeof(llt))); + uint32_t iaid = 7; // Just a number + SubnetID subnet_id = 8; // Just another number + + for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) { + IOAddress addr(ADDRESS[i]); + Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, + duid, iaid, 100, 200, 50, 80, + subnet_id)); + + EXPECT_TRUE(lease->addr_ == addr); + EXPECT_TRUE(*lease->duid_ == *duid); + EXPECT_TRUE(lease->iaid_ == iaid); + EXPECT_TRUE(lease->subnet_id_ == subnet_id); + EXPECT_TRUE(lease->type_ == Lease::TYPE_NA); + EXPECT_TRUE(lease->preferred_lft_ == 100); + EXPECT_TRUE(lease->valid_lft_ == 200); + EXPECT_TRUE(lease->t1_ == 50); + EXPECT_TRUE(lease->t2_ == 80); + EXPECT_FALSE(lease->fqdn_fwd_); + EXPECT_FALSE(lease->fqdn_rev_); + EXPECT_TRUE(lease->hostname_.empty()); + + } + + // Lease6 must be instantiated with a DUID, not with NULL pointer + IOAddress addr(ADDRESS[0]); + Lease6Ptr lease2; + EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr, + DuidPtr(), iaid, 100, 200, 50, 80, + subnet_id)), InvalidOperation); +} + +// This test verifies that the Lease6 constructor which accepts FQDN data, +// sets the data correctly for the lease. +TEST(Lease6, Lease6ConstructorWithFQDN) { + + // check a variety of addresses with different bits set. + const char* ADDRESS[] = { + "::", "::1", "2001:db8:1::456", + "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "8000::", "8000::1", + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + }; + + // Other values + uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + DuidPtr duid(new DUID(llt, sizeof(llt))); + uint32_t iaid = 7; // Just a number + SubnetID subnet_id = 8; // Just another number + + for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) { + IOAddress addr(ADDRESS[i]); + Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, + duid, iaid, 100, 200, 50, 80, subnet_id, + true, true, "host.example.com.")); + + EXPECT_TRUE(lease->addr_ == addr); + EXPECT_TRUE(*lease->duid_ == *duid); + EXPECT_TRUE(lease->iaid_ == iaid); + EXPECT_TRUE(lease->subnet_id_ == subnet_id); + EXPECT_TRUE(lease->type_ == Lease::TYPE_NA); + EXPECT_TRUE(lease->preferred_lft_ == 100); + EXPECT_TRUE(lease->valid_lft_ == 200); + EXPECT_TRUE(lease->t1_ == 50); + EXPECT_TRUE(lease->t2_ == 80); + EXPECT_TRUE(lease->fqdn_fwd_); + EXPECT_TRUE(lease->fqdn_rev_); + EXPECT_EQ("host.example.com.", lease->hostname_); + } + + // Lease6 must be instantiated with a DUID, not with NULL pointer + IOAddress addr(ADDRESS[0]); + Lease6Ptr lease2; + EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr, + DuidPtr(), iaid, 100, 200, 50, 80, + subnet_id)), InvalidOperation); +} + +// This test verifies that the matches() function returns true if two leases +// differ by values other than address, type, prefix length, IAID and DUID. +TEST(Lease6, matches) { + + // Create two matching leases. + uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + DuidPtr duid(new DUID(llt, sizeof(llt))); + + Lease6 lease1(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid, + IAID, 100, 200, 50, 80, + SUBNET_ID); + lease1.hostname_ = "lease1.example.com."; + lease1.fqdn_fwd_ = true; + lease1.fqdn_rev_ = true; + Lease6 lease2(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid, + IAID, 200, 300, 90, 70, + SUBNET_ID); + lease2.hostname_ = "lease1.example.com."; + lease2.fqdn_fwd_ = false; + lease2.fqdn_rev_ = true; + + EXPECT_TRUE(lease1.matches(lease2)); + + // Modify each value used to match both leases, and make sure that + // leases don't match. + + // Modify address. + lease1.addr_ = IOAddress("2001:db8:1::2"); + EXPECT_FALSE(lease1.matches(lease2)); + lease1.addr_ = lease2.addr_; + + // Modify lease type. + lease1.type_ = Lease6::TYPE_TA; + EXPECT_FALSE(lease1.matches(lease2)); + lease1.type_ = lease2.type_; + + // Modify prefix length. + lease1.prefixlen_ += 1; + EXPECT_FALSE(lease1.matches(lease2)); + lease1.prefixlen_ = lease2.prefixlen_; + + // Modify IAID. + lease1.iaid_ += 1; + EXPECT_FALSE(lease1.matches(lease2)); + lease1.iaid_ = lease2.iaid_; + + // Modify DUID. + llt[1] += 1; + duid.reset(new DUID(llt, sizeof(llt))); + lease1.duid_ = duid; + EXPECT_FALSE(lease1.matches(lease2)); + lease1.duid_ = lease2.duid_; +} + +/// @brief Lease6 Equality Test +/// +/// Checks that the operator==() correctly compares two leases for equality. +/// As operator!=() is also defined for this class, every check on operator==() +/// is followed by the reverse check on operator!=(). +TEST(Lease6, OperatorEquals) { + + // check a variety of addresses with different bits set. + const IOAddress addr("2001:db8:1::456"); + uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + DuidPtr duid(new DUID(duid_array, sizeof(duid_array))); + uint32_t iaid = 7; // just a number + SubnetID subnet_id = 8; // just another number + + // Check for equality. + Lease6 lease1(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80, + subnet_id); + Lease6 lease2(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80, + subnet_id); + + // cltt_ constructs with time(NULL), make sure they are always equal + lease1.cltt_ = lease2.cltt_; + + EXPECT_TRUE(lease1 == lease2); + EXPECT_FALSE(lease1 != lease2); + + // Go through and alter all the fields one by one + + lease1.addr_ = IOAddress("::1"); + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.addr_ = lease2.addr_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.type_ = Lease::TYPE_PD; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.type_ = lease2.type_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.prefixlen_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.prefixlen_ = lease2.prefixlen_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.iaid_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.iaid_ = lease2.iaid_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++duid_array[0]; + lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array))); + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + --duid_array[0]; + lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array))); + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.preferred_lft_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.preferred_lft_ = lease2.preferred_lft_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.valid_lft_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.valid_lft_ = lease2.valid_lft_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.t1_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.t1_ = lease2.t1_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.t2_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.t2_ = lease2.t2_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.cltt_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.cltt_ = lease2.cltt_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + ++lease1.subnet_id_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.subnet_id_ = lease2.subnet_id_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.fixed_ = !lease1.fixed_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.fixed_ = lease2.fixed_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.hostname_ += std::string("Something random"); + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.hostname_ = lease2.hostname_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.fqdn_fwd_ = !lease1.fqdn_fwd_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.fqdn_fwd_ = lease2.fqdn_fwd_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.fqdn_rev_ = !lease1.fqdn_rev_; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.fqdn_rev_ = lease2.fqdn_rev_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.comments_ += std::string("Something random"); + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease1.comments_ = lease2.comments_; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal +} + +// Checks if lease expiration is calculated properly +TEST(Lease6, Lease6Expired) { + const IOAddress addr("2001:db8:1::456"); + const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + const DuidPtr duid(new DUID(duid_array, sizeof(duid_array))); + const uint32_t iaid = 7; // Just a number + const SubnetID subnet_id = 8; // Just another number + Lease6 lease(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80, + subnet_id); + + // Case 1: a second before expiration + lease.cltt_ = time(NULL) - 100; + lease.valid_lft_ = 101; + EXPECT_FALSE(lease.expired()); + + // Case 2: the lease will expire after this second is concluded + lease.cltt_ = time(NULL) - 101; + EXPECT_FALSE(lease.expired()); + + // Case 3: the lease is expired + lease.cltt_ = time(NULL) - 102; + EXPECT_TRUE(lease.expired()); +} + // Verify that the DUID can be returned as a vector object and if DUID is NULL // the empty vector is returned. -TEST(Lease6Test, getDuidVector) { +TEST(Lease6, getDuidVector) { // Create a lease. Lease6 lease; // By default, the lease should have client id set to NULL. If it doesn't, @@ -67,5 +733,21 @@ TEST(Lease6Test, getDuidVector) { EXPECT_TRUE(returned_vec == duid_vec); } +// Verify the behavior of the function which checks FQDN data for equality. +TEST(Lease6, hasIdenticalFqdn) { + Lease6 lease = createLease6("myhost.example.com.", true, true); + EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.", + true, true))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("other.example.com.", + true, true))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.", + false, true))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.", + true, false))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.", + false, false))); + EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("other.example.com.", + false, false))); +} }; // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index 85015f623d..657075001f 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.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 @@ -95,7 +95,7 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) { IOAddress("2001:db8:1::456")); ASSERT_TRUE(x); - EXPECT_EQ(x->addr_.toText(), addr.toText()); + EXPECT_EQ(x->addr_, addr); EXPECT_TRUE(*x->duid_ == *duid); EXPECT_EQ(x->iaid_, iaid); EXPECT_EQ(x->subnet_id_, subnet_id); @@ -114,7 +114,7 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) { ASSERT_TRUE(y); EXPECT_TRUE(*y->duid_ == *duid); EXPECT_EQ(y->iaid_, iaid); - EXPECT_EQ(y->addr_.toText(), addr.toText()); + EXPECT_EQ(y->addr_, addr); // Test getLease6(duid, iaid, subnet_id) - wrong iaid uint32_t invalid_iaid = 9; // no such iaid @@ -144,7 +144,7 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) { EXPECT_EQ(Lease6Ptr(), x); } -// @todo Write more memfile tests +/// @todo Write more memfile tests // Simple test about lease4 retrieval through client id method TEST_F(MemfileLeaseMgrTest, getLease4ClientId) { diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index d5e00ab1ea..578ef3c089 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.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 @@ -549,7 +549,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4Hwaddr) { } // Get the leases matching the hardware address of lease 1 - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented HWAddr tmp(leases[1]->hwaddr_, HTYPE_ETHER); Lease4Collection returned = lmptr_->getLease4(tmp); @@ -568,14 +568,14 @@ TEST_F(MySqlLeaseMgrTest, getLease4Hwaddr) { EXPECT_EQ(straddress4_[5], addresses[2]); // Repeat test with just one expected match - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented returned = lmptr_->getLease4(HWAddr(leases[2]->hwaddr_, HTYPE_ETHER)); ASSERT_EQ(1, returned.size()); detailCompareLease(leases[2], *returned.begin()); // Check that an empty vector is valid EXPECT_TRUE(leases[7]->hwaddr_.empty()); - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented returned = lmptr_->getLease4(HWAddr(leases[7]->hwaddr_, HTYPE_ETHER)); ASSERT_EQ(1, returned.size()); detailCompareLease(leases[7], *returned.begin()); @@ -599,7 +599,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSize) { for (uint8_t i = 0; i <= HWAddr::MAX_HWADDR_LEN; ++i) { leases[1]->hwaddr_.resize(i, i); EXPECT_TRUE(lmptr_->addLease(leases[1])); - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented Lease4Collection returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, HTYPE_ETHER)); @@ -610,7 +610,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSize) { // Database should not let us add one that is too big // (The 42 is a random value put in each byte of the address.) - // @todo: 2589 will make this test impossible + /// @todo: 2589 will make this test impossible leases[1]->hwaddr_.resize(HWAddr::MAX_HWADDR_LEN + 100, 42); EXPECT_THROW(lmptr_->addLease(leases[1]), isc::dhcp::DbOperationError); } @@ -628,7 +628,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) { // Get the leases matching the hardware address of lease 1 and // subnet ID of lease 1. Result should be a single lease - lease 1. - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented Lease4Ptr returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, HTYPE_ETHER), leases[1]->subnet_id_); @@ -637,7 +637,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) { // Try for a match to the hardware address of lease 1 and the wrong // subnet ID. - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, HTYPE_ETHER), leases[1]->subnet_id_ + 1); EXPECT_FALSE(returned); @@ -645,14 +645,14 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) { // Try for a match to the subnet ID of lease 1 (and lease 4) but // the wrong hardware address. vector<uint8_t> invalid_hwaddr(15, 0x77); - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented returned = lmptr_->getLease4(HWAddr(invalid_hwaddr, HTYPE_ETHER), leases[1]->subnet_id_); EXPECT_FALSE(returned); // Try for a match to an unknown hardware address and an unknown // subnet ID. - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented returned = lmptr_->getLease4(HWAddr(invalid_hwaddr, HTYPE_ETHER), leases[1]->subnet_id_ + 1); EXPECT_FALSE(returned); @@ -665,7 +665,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) { EXPECT_TRUE(lmptr_->deleteLease(leases[2]->addr_)); leases[1]->addr_ = leases[2]->addr_; EXPECT_TRUE(lmptr_->addLease(leases[1])); - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented EXPECT_THROW(returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, HTYPE_ETHER), leases[1]->subnet_id_), @@ -687,7 +687,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetIdSize) { for (uint8_t i = 0; i <= HWAddr::MAX_HWADDR_LEN; ++i) { leases[1]->hwaddr_.resize(i, i); EXPECT_TRUE(lmptr_->addLease(leases[1])); - // @todo: Simply use HWAddr directly once 2589 is implemented + /// @todo: Simply use HWAddr directly once 2589 is implemented Lease4Ptr returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, HTYPE_ETHER), leases[1]->subnet_id_); diff --git a/src/lib/dhcpsrv/tests/test_utils.cc b/src/lib/dhcpsrv/tests/test_utils.cc index e418c6208d..44d60ea4ff 100644 --- a/src/lib/dhcpsrv/tests/test_utils.cc +++ b/src/lib/dhcpsrv/tests/test_utils.cc @@ -48,7 +48,7 @@ detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second) { // odd things happen when they are different: the EXPECT_EQ macro appears to // call the operator uint32_t() function, which causes an exception to be // thrown for IPv6 addresses. - EXPECT_EQ(first->addr_.toText(), second->addr_.toText()); + EXPECT_EQ(first->addr_, second->addr_); EXPECT_TRUE(first->hwaddr_ == second->hwaddr_); if (first->client_id_ && second->client_id_) { EXPECT_TRUE(*first->client_id_ == *second->client_id_); @@ -83,7 +83,7 @@ detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second) { // odd things happen when they are different: the EXPECT_EQ macro appears to // call the operator uint32_t() function, which causes an exception to be // thrown for IPv6 addresses. - EXPECT_EQ(first->addr_.toText(), second->addr_.toText()); + EXPECT_EQ(first->addr_, second->addr_); EXPECT_EQ(first->prefixlen_, second->prefixlen_); EXPECT_EQ(first->iaid_, second->iaid_); ASSERT_TRUE(first->duid_); |