Commit b526ece8 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3035] Generate NameChangeRequest when new lease is acquired.

parent cafeb725
...@@ -570,43 +570,48 @@ Dhcpv4Srv::srvidToString(const OptionPtr& srvid) { ...@@ -570,43 +570,48 @@ Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
} }
isc::dhcp_ddns::D2Dhcid isc::dhcp_ddns::D2Dhcid
Dhcpv4Srv::computeDhcid(const Pkt4Ptr& query, const Pkt4Ptr& answer) { Dhcpv4Srv::computeDhcid(const Lease4Ptr& lease) {
std::vector<uint8_t> dhcid_data(1); if (!lease) {
OptionPtr client_id = answer->getOption(DHO_DHCP_CLIENT_IDENTIFIER); isc_throw(DhcidComputeError, "a pointer to the lease must be not"
if (client_id) { " NULL to compute DHCID");
dhcid_data.push_back(1);
dhcid_data.insert(dhcid_data.end(), client_id->getData().begin(), } else if (lease->hostname_.empty()) {
client_id->getData().end()); isc_throw(DhcidComputeError, "unable to compute the DHCID for the"
} else { " lease which has empty hostname set");
HWAddrPtr hwaddr = query->getHWAddr();
dhcid_data.push_back(0);
dhcid_data.push_back(hwaddr->htype_);
dhcid_data.insert(dhcid_data.end(), hwaddr->hwaddr_.begin(),
hwaddr->hwaddr_.end());
} }
std::string domain_name; // In order to compute DHCID the client's hostname must be encoded in
Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn> // canonical wire format. It is unlikely that the FQDN is malformed
(answer->getOption(DHO_FQDN)); // because it is validated by the classes which encapsulate options
if (fqdn) { // carrying client FQDN. However, if the client name was carried in the
domain_name = fqdn->getDomainName(); // Hostname option it is more likely as it carries the hostname as a
// regular string.
std::vector<uint8_t> fqdn_wire;
try {
OptionDataTypeUtil::writeFqdn(lease->hostname_, fqdn_wire, true);
} else { } catch (const Exception& ex) {
OptionCustomPtr hostname = boost::dynamic_pointer_cast<OptionCustom> isc_throw(DhcidComputeError, "unable to compute DHCID because the"
(answer->getOption(DHO_HOST_NAME)); " hostname: " << lease->hostname_ << " is invalid");
if (hostname) {
domain_name = hostname->readString();
}
} }
// Prefer client id to HW address to compute DHCID. If Client Id is
// NULL, use HW address.
try { try {
OptionDataTypeUtil::writeFqdn(domain_name, dhcid_data, true); if (lease->client_id_) {
return (D2Dhcid(lease->client_id_->getClientId(), fqdn_wire));
} else {
HWAddrPtr hwaddr(new HWAddr(lease->hwaddr_, HTYPE_ETHER));
return (D2Dhcid(hwaddr, fqdn_wire));
}
} catch (const Exception& ex) { } catch (const Exception& ex) {
; isc_throw(DhcidComputeError, "unable to compute DHCID: "
} << ex.what());
D2Dhcid dhcid(dhcid_data); }
return (dhcid);
} }
...@@ -840,6 +845,21 @@ void ...@@ -840,6 +845,21 @@ void
Dhcpv4Srv::processHostnameOption(const Pkt4Ptr&, Pkt4Ptr&) { Dhcpv4Srv::processHostnameOption(const Pkt4Ptr&, Pkt4Ptr&) {
} }
void
Dhcpv4Srv::createNameChangeRequests(const Lease4Ptr& lease,
const Lease4Ptr&) {
if (!lease) {
isc_throw(isc::Unexpected,
"NULL lease specified when creating NameChangeRequest");
}
D2Dhcid dhcid = computeDhcid(lease);
NameChangeRequest ncr(isc::dhcp_ddns::CHG_ADD,
lease->fqdn_fwd_, lease->fqdn_rev_, lease->hostname_,
lease->addr_.toText(), dhcid, 0, lease->valid_lft_);
name_change_reqs_.push(ncr);
}
void void
Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) { Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
......
...@@ -27,10 +27,18 @@ ...@@ -27,10 +27,18 @@
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <iostream> #include <iostream>
#include <queue>
namespace isc { namespace isc {
namespace dhcp { namespace dhcp {
/// @brief Exception thrown when DHCID computation failed.
class DhcidComputeError : public isc::Exception {
public:
DhcidComputeError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief DHCPv4 server service. /// @brief DHCPv4 server service.
/// ///
/// This singleton class represents DHCPv4 server. It contains all /// This singleton class represents DHCPv4 server. It contains all
...@@ -303,6 +311,24 @@ private: ...@@ -303,6 +311,24 @@ private:
void processHostnameOption(const Pkt4Ptr& query, Pkt4Ptr& answer); void processHostnameOption(const Pkt4Ptr& query, Pkt4Ptr& answer);
protected: protected:
/// @brief Creates NameChangeRequests which correspond to the lease
/// which has been acquired.
///
/// If this function is called whe an existing lease is renewed, it
/// may generate NameChangeRequest to remove existing DNS entries which
/// correspond to the old lease instance. This function may cease to
/// generate NameChangeRequests if the notion of the client's FQDN hasn't
/// changed between an old and new lease.
///
/// @param lease A pointer to the new lease which has been acquired.
/// @param old_lease A pointer to the instance of the old lease which has
/// been replaced by the new lease passed in the first argument. The NULL
/// value indicates that the new lease has been allocated, rather than
/// lease being renewed.
void createNameChangeRequests(const Lease4Ptr& lease,
const Lease4Ptr& old_lease);
/// @brief Attempts to renew received addresses /// @brief Attempts to renew received addresses
/// ///
/// Attempts to renew existing lease. This typically includes finding a lease that /// Attempts to renew existing lease. This typically includes finding a lease that
...@@ -377,15 +403,11 @@ protected: ...@@ -377,15 +403,11 @@ protected:
/// @return string representation /// @return string representation
static std::string srvidToString(const OptionPtr& opt); static std::string srvidToString(const OptionPtr& opt);
/// @brief Computes DHCID using options stored in the response message /// @brief Computes DHCID from a lease.
/// to a client.
/// ///
/// @param query An object encapsulating client's message to the server. /// @param lease A pointer to the structure describing a lease.
/// @param answer An object encapsulating response message being sent to
/// a client.
/// @return An object encapsulating DHCID to be used for DNS updates. /// @return An object encapsulating DHCID to be used for DNS updates.
static isc::dhcp_ddns::D2Dhcid computeDhcid(const Pkt4Ptr& query, static isc::dhcp_ddns::D2Dhcid computeDhcid(const Lease4Ptr& lease);
const Pkt4Ptr& answer);
/// @brief Selects a subnet for a given client's packet. /// @brief Selects a subnet for a given client's packet.
/// ///
...@@ -433,6 +455,12 @@ private: ...@@ -433,6 +455,12 @@ private:
int hook_index_pkt4_receive_; int hook_index_pkt4_receive_;
int hook_index_subnet4_select_; int hook_index_subnet4_select_;
int hook_index_pkt4_send_; int hook_index_pkt4_send_;
protected:
/// Holds a list of @c isc::dhcp_ddns::NameChangeRequest objects which
/// are waiting for sending to b10-dhcp-ddns module.
std::queue<isc::dhcp_ddns::NameChangeRequest> name_change_reqs_;
}; };
}; // namespace isc::dhcp }; // namespace isc::dhcp
......
...@@ -127,6 +127,8 @@ public: ...@@ -127,6 +127,8 @@ public:
std::list<Pkt4Ptr> fake_sent_; std::list<Pkt4Ptr> fake_sent_;
using Dhcpv4Srv::adjustRemoteAddr; using Dhcpv4Srv::adjustRemoteAddr;
using Dhcpv4Srv::computeDhcid;
using Dhcpv4Srv::createNameChangeRequests;
using Dhcpv4Srv::processDiscover; using Dhcpv4Srv::processDiscover;
using Dhcpv4Srv::processRequest; using Dhcpv4Srv::processRequest;
using Dhcpv4Srv::processRelease; using Dhcpv4Srv::processRelease;
...@@ -139,6 +141,7 @@ public: ...@@ -139,6 +141,7 @@ public:
using Dhcpv4Srv::writeServerID; using Dhcpv4Srv::writeServerID;
using Dhcpv4Srv::sanityCheck; using Dhcpv4Srv::sanityCheck;
using Dhcpv4Srv::srvidToString; using Dhcpv4Srv::srvidToString;
using Dhcpv4Srv::name_change_reqs_;
}; };
/// @brief Dummy Packet Filtering class. /// @brief Dummy Packet Filtering class.
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <asiolink/io_address.h> #include <asiolink/io_address.h>
#include <dhcp/option4_client_fqdn.h> #include <dhcp/option4_client_fqdn.h>
#include <dhcp4/tests/dhcp4_test_utils.h> #include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp_ddns/ncr_msg.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
...@@ -23,6 +24,7 @@ ...@@ -23,6 +24,7 @@
using namespace isc; using namespace isc;
using namespace isc::asiolink; using namespace isc::asiolink;
using namespace isc::dhcp; using namespace isc::dhcp;
using namespace isc::dhcp_ddns;
using namespace isc::test; using namespace isc::test;
namespace { namespace {
...@@ -36,6 +38,24 @@ public: ...@@ -36,6 +38,24 @@ public:
delete srv_; delete srv_;
} }
// Create a lease to be used by various tests.
Lease4Ptr createLease(const isc::asiolink::IOAddress& addr,
const std::string& hostname,
const bool fqdn_fwd,
const bool fqdn_rev) {
const uint8_t hwaddr[] = { 0, 1, 2, 3, 4, 5, 6 };
Lease4Ptr lease(new Lease4(addr, hwaddr, sizeof(hwaddr),
&generateClientId()->getData()[0],
generateClientId()->getData().size(),
100, 50, 75, time(NULL), subnet_->getID()));
// @todo Set this through the Lease4 constructor.
lease->hostname_ = hostname;
lease->fqdn_fwd_ = fqdn_fwd;
lease->fqdn_rev_ = fqdn_rev;
return (lease);
}
// Create an instance of the DHCPv4 Client FQDN Option. // Create an instance of the DHCPv4 Client FQDN Option.
Option4ClientFqdnPtr Option4ClientFqdnPtr
createClientFqdn(const uint8_t flags, createClientFqdn(const uint8_t flags,
...@@ -59,7 +79,8 @@ public: ...@@ -59,7 +79,8 @@ public:
const std::string& fqdn_domain_name, const std::string& fqdn_domain_name,
const Option4ClientFqdn::DomainNameType const Option4ClientFqdn::DomainNameType
fqdn_type, fqdn_type,
const bool include_prl) { const bool include_prl,
const bool include_clientid = true) {
Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234)); Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234));
pkt->setRemoteAddr(IOAddress("192.0.2.3")); pkt->setRemoteAddr(IOAddress("192.0.2.3"));
// For DISCOVER we don't include server id, because client broadcasts // For DISCOVER we don't include server id, because client broadcasts
...@@ -67,8 +88,10 @@ public: ...@@ -67,8 +88,10 @@ public:
if (msg_type != DHCPDISCOVER) { if (msg_type != DHCPDISCOVER) {
pkt->addOption(srv_->getServerID()); pkt->addOption(srv_->getServerID());
} }
// Client id is required.
pkt->addOption(generateClientId()); if (include_clientid) {
pkt->addOption(generateClientId());
}
// Create Client FQDN Option with the specified flags and // Create Client FQDN Option with the specified flags and
// domain-name. // domain-name.
...@@ -121,11 +144,87 @@ public: ...@@ -121,11 +144,87 @@ public:
} }
private: // Verify that NameChangeRequest holds valid values.
void verifyNameChangeRequest(const isc::dhcp_ddns::NameChangeType type,
const bool reverse, const bool forward,
const std::string& addr,
const std::string& dhcid,
const uint16_t expires,
const uint16_t len) {
NameChangeRequest ncr = srv_->name_change_reqs_.front();
EXPECT_EQ(type, ncr.getChangeType());
EXPECT_EQ(forward, ncr.isForwardChange());
EXPECT_EQ(reverse, ncr.isReverseChange());
EXPECT_EQ(addr, ncr.getIpAddress());
EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
EXPECT_EQ(len, ncr.getLeaseLength());
EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
srv_->name_change_reqs_.pop();
}
NakedDhcpv4Srv* srv_; NakedDhcpv4Srv* srv_;
}; };
// Test that the exception is thrown if lease pointer specified as the argument
// of computeDhcid function is NULL.
TEST_F(FqdnDhcpv4SrvTest, dhcidNullLease) {
Lease4Ptr lease;
EXPECT_THROW(srv_->computeDhcid(lease), isc::dhcp::DhcidComputeError);
}
// Test that the appropriate exception is thrown if the lease object used
// to compute DHCID comprises wrong hostname.
TEST_F(FqdnDhcpv4SrvTest, dhcidWrongHostname) {
// First, make sure that the lease with the correct hostname is accepted.
Lease4Ptr lease = createLease(IOAddress("192.0.2.3"),
"myhost.example.com.", true, true);
ASSERT_NO_THROW(srv_->computeDhcid(lease));
// Now, use the wrong hostname. It should result in the exception.
lease->hostname_ = "myhost...example.com.";
EXPECT_THROW(srv_->computeDhcid(lease), isc::dhcp::DhcidComputeError);
// Also, empty hostname is wrong.
lease->hostname_ = "";
EXPECT_THROW(srv_->computeDhcid(lease), isc::dhcp::DhcidComputeError);
}
// Test that the DHCID is computed correctly, when the lease holds
// correct hostname and non-NULL client id.
TEST_F(FqdnDhcpv4SrvTest, dhcidComputeFromClientId) {
Lease4Ptr lease = createLease(IOAddress("192.0.2.3"),
"myhost.example.com.",
true, true);
isc::dhcp_ddns::D2Dhcid dhcid;
ASSERT_NO_THROW(dhcid = srv_->computeDhcid(lease));
// Make sure that the computed DHCID is valid.
std::string dhcid_ref = "00010132E91AA355CFBB753C0F0497A5A9404"
"36965B68B6D438D98E680BF10B09F3BCF";
EXPECT_EQ(dhcid_ref, dhcid.toStr());
}
// Test that the DHCID is computed correctly, when the lease holds correct
// hostname and NULL client id.
TEST_F(FqdnDhcpv4SrvTest, dhcidComputeFromHWAddr) {
Lease4Ptr lease = createLease(IOAddress("192.0.2.3"),
"myhost.example.com.",
true, true);
lease->client_id_.reset();
isc::dhcp_ddns::D2Dhcid dhcid;
ASSERT_NO_THROW(dhcid = srv_->computeDhcid(lease));
// Make sure that the computed DHCID is valid.
std::string dhcid_ref = "0000012247F6DC4423C3E8627434A9D6868609"
"D88948F78018B215EDCAA30C0C135035";
EXPECT_EQ(dhcid_ref, dhcid.toStr());
}
// Test that server confirms to perform the forward and reverse DNS update, // Test that server confirms to perform the forward and reverse DNS update,
// when client asks for it. // when client asks for it.
TEST_F(FqdnDhcpv4SrvTest, serverUpdateForward) { TEST_F(FqdnDhcpv4SrvTest, serverUpdateForward) {
...@@ -201,6 +300,34 @@ TEST_F(FqdnDhcpv4SrvTest, clientUpdateNotAllowed) { ...@@ -201,6 +300,34 @@ TEST_F(FqdnDhcpv4SrvTest, clientUpdateNotAllowed) {
} }
// Test that exactly one NameChangeRequest is generated when the new lease
// has been acquired (old lease is NULL).
TEST_F(FqdnDhcpv4SrvTest, createNameChangeRequestsNewLease) {
Lease4Ptr lease = createLease(IOAddress("192.0.2.3"), "myhost.example.com.",
true, true);
Lease4Ptr old_lease;
ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease));
ASSERT_EQ(1, srv_->name_change_reqs_.size());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
"192.0.2.3", "00010132E91AA355CFBB753C0F049"
"7A5A940436965B68B6D438D98E680BF10B09F3BCF",
0, 100);
}
// Test that no NameChangeRequest is generated when a lease is renewed and
// the FQDN data hasn't changed.
TEST_F(FqdnDhcpv4SrvTest, createNameChangeRequestsRenewNoChange) {
Lease4Ptr lease = createLease(IOAddress("192.0.2.3"), "myhost.example.com.",
true, true);
Lease4Ptr old_lease = createLease(IOAddress("192.0.2.3"),
"myhost.example.com.", true, true);
old_lease->valid_lft_ += 100;
ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease));
EXPECT_TRUE(srv_->name_change_reqs_.empty());
}
} // end of anonymous namespace } // end of anonymous namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment