]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#544] leaseX-del commands may now update DNS
authorThomas Markwalder <tmark@isc.org>
Fri, 17 Jul 2020 15:56:15 +0000 (11:56 -0400)
committerThomas Markwalder <tmark@isc.org>
Fri, 17 Jul 2020 15:56:15 +0000 (11:56 -0400)
doc/sphinx/arm/hooks-lease-cmds.rst
    Updated leaseX-del documentation

src/hooks/dhcp/lease_cmds/lease_cmds.*
    LeaseCmdsImp::Parameters
    LeaseCmdsImpl::getParameters()
    - Added supportr for update-ddns

    LeaseCmdsImpl::lease4DelHandler()
    LeaseCmdsImpl::lease6DelHandler()
    - Added call t queueNCR()

src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc
    TEST_F(LeaseCmdsTest, LeaseXDelBadUpdateDdnsParam)
    TEST_F(LeaseCmdsTest, lease4DnsRemoveD2Enabled)
    TEST_F(LeaseCmdsTest, lease4DnsRemoveD2Disabled)
    TEST_F(LeaseCmdsTest, lease6DnsRemoveD2Enabled)
    TEST_F(LeaseCmdsTest, lease6DnsRemoveD2Disabled)
    - new tests

Added a ChangeLog entry

ChangeLog
doc/sphinx/arm/hooks-lease-cmds.rst
src/hooks/dhcp/lease_cmds/lease_cmds.cc
src/hooks/dhcp/lease_cmds/lease_cmds.h
src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc
src/lib/dhcpsrv/dhcpsrv_messages.cc
src/lib/dhcpsrv/dhcpsrv_messages.h
src/lib/dhcpsrv/memfile_lease_mgr.cc

index 1105128f2c2c232ba919f3d2033b2cf29eb961da..e4835971abca9f96d1cbf567aff8afc703fe0cbd 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+1774.  [func]          tmark
+       leaseX-del commands now support a new parameter, update-ddns,
+       which instructs the server to remove DNS entries for a
+       lease after it has been deleted.
+       (Gitlab #544)
+
 1773.  [perf]          fdupont
        Kea statistics now uses standard c++11 chrono library instead
        of POSIX time library from boost.
index f801e2b9b59f5c5b57f6e0ba08c275253251967b..31d0a2f8536a2054840ba597d5760d53718ef9f4 100644 (file)
@@ -777,7 +777,7 @@ a pair of values: the type and the actual identifier. The currently
 supported identifiers are "hw-address" (IPv4 only), "client-id" (IPv4
 only), and "duid" (IPv6 only).
 
-An example command for deleting a lease by address is:
+An example command for deleting a lease by address is
 
 ::
 
@@ -801,6 +801,28 @@ An example IPv4 lease deletion by "hw-address" is:
      }
    }
 
+
+As of Kea 1.7.9, a new parameter, "update-ddns", is supported (IPv4 and IPv6).
+When ```true``` it instructs the server to queue a request to kea-dhcp-ddns to
+remove DNS entries after the lease is succesfully deleted if:
+
+- DDNS updating is enabled. (i.e. "dhcp-ddns":{ "enable-updates": true"})
+- The lease's hostname is not be empty.
+- At least one of the lease's DNS direction flags (fdqn_fwd or fdqn_rev) is true.
+
+This parameter defaults to false. An example of its use is shown below:
+
+::
+
+   {
+       "command": "lease4-del",
+       "arguments": {
+           "ip-address": "192.0.2.202",
+           "update-ddns": true
+       }
+   }
+
+
 ``leaseX-del`` returns a result that indicates the outcome of the
 operation. It has one of the following values: 0 (success), 1 (error),
 or 3 (empty). The empty result means that a query has been completed
index 7b2d58e5d3fd6befb3123a566002fbc54cadebfd..0cc57d6c201842baac084e2672349ed38ad4054f 100644 (file)
@@ -111,10 +111,13 @@ public:
         /// @brief IAID identifier used for v6 leases
         uint32_t iaid;
 
+        /// @brief Indicates whether or not DNS should be updated.
+        bool updateDDNS;
+
         /// @brief Default constructor.
         Parameters()
             : addr("::"), query_type(TYPE_ADDR), lease_type(Lease::TYPE_NA),
-              iaid(0) {
+              iaid(0), updateDDNS(false) {
         }
     };
 
@@ -484,6 +487,15 @@ LeaseCmdsImpl::getParameters(bool v6, const ConstElementPtr& params) {
         isc_throw(BadValue, "Parameters missing or are not a map.");
     }
 
+    if (params->contains("update-ddns")) {
+        ConstElementPtr tmp = params->get("update-ddns");
+        if (tmp->getType() != Element::boolean) {
+            isc_throw(BadValue, "'update-ddns' is not a boolean");
+        } else {
+            x.updateDDNS = tmp->boolValue();
+        }
+    }
+
     // We support several sets of parameters for leaseX-get/lease-del:
     // lease-get(type, address)
     // lease-get(type, subnet-id, identifier-type, identifier)
@@ -581,6 +593,7 @@ LeaseCmdsImpl::getParameters(bool v6, const ConstElementPtr& params) {
                   " is not supported.");
     }
     }
+
     return (x);
 }
 
@@ -1157,6 +1170,12 @@ LeaseCmdsImpl::lease4DelHandler(CalloutHandle& handle) {
         } else {
             setErrorResponse (handle, "IPv4 lease not found.", CONTROL_RESULT_EMPTY);
         }
+
+        // Queue an NCR to remove DNS if configured and the lease has it.
+        if (p.updateDDNS) {
+            queueNCR(CHG_REMOVE, lease4);
+        }
+
     } catch (const std::exception& ex) {
         setErrorResponse(handle, ex.what());
         return (1);
@@ -1435,6 +1454,12 @@ LeaseCmdsImpl::lease6DelHandler(CalloutHandle& handle) {
         } else {
             setErrorResponse (handle, "IPv6 lease not found.", CONTROL_RESULT_EMPTY);
         }
+
+        // Queue an NCR to remove DNS if configured and the lease has it.
+        if (p.updateDDNS) {
+            queueNCR(CHG_REMOVE, lease6);
+        }
+
     } catch (const std::exception& ex) {
         setErrorResponse(handle, ex.what());
         return (1);
index 0747fb0292fc3287820238b5b947d12c0b0cc6d7..63f29606e370230461adbeceaaa31201ce157efb 100644 (file)
@@ -345,7 +345,9 @@ public:
     /// criteria.
     /// It extracts the command name and arguments from the given Callouthandle,
     /// attempts to process them, and then set's the handle's "response"
-    /// argument accordingly.
+    /// argument accordingly.  If the lease is deleted successfully, then a call
+    /// to @ref isc::dhcp::queueNCR() is issued, which to generate an
+    /// CHG_REMOVE request to kea-dhcp-ddns, if appropriate.
     ///
     /// Two types of parameters are supported: (subnet-id, address) or
     /// (subnet-id, identifier-type, identifier).
@@ -381,7 +383,9 @@ public:
     /// This command attempts to delete a lease that match selected criteria.
     /// It extracts the command name and arguments from the given Callouthandle,
     /// attempts to process them, and then set's the handle's "response"
-    /// argument accordingly.
+    /// argument accordingly.  If the lease is deleted successfully, then a call
+    /// to @ref isc::dhcp::queueNCR() is issued, which to generate an
+    /// CHG_REMOVE request to kea-dhcp-ddns, if appropriate.
     ///
     /// Two types of parameters are supported: (subnet-id, address) or
     /// (subnet-id, type, iaid, identifier-type, identifier).
index 5e3178b1b50e3753aab0dba6e2f6658bee7c9220..b36a1b498834d0466957dcc043aec19ba0bfe57c 100644 (file)
@@ -4034,6 +4034,33 @@ TEST_F(LeaseCmdsTest, Lease4DelByAddr) {
     EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
 }
 
+// Checks that leaseX-del checks update-ddns input
+TEST_F(LeaseCmdsTest, LeaseXDelBadUpdateDdnsParam) {
+
+    string cmd =
+        "{\n"
+        "    \"command\": \"lease4-del\",\n"
+        "    \"arguments\": {"
+        "        \"ip-address\": \"192.0.1.0\","
+        "        \"update-ddns\": 77"
+        "    }\n"
+        "}";
+
+    string exp_rsp = "'update-ddns' is not a boolean";
+    testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+    cmd =
+        "{\n"
+        "    \"command\": \"lease6-del\",\n"
+        "    \"arguments\": {"
+        "        \"ip-address\": \"2001:db8:1::1\","
+        "        \"update-ddns\": \"bogus\""
+        "    }\n"
+        "}";
+
+    exp_rsp = "'update-ddns' is not a boolean";
+    testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
 
 // Checks that lease4-del sanitizes its input.
 TEST_F(LeaseCmdsTest, Lease4DelByAddrBadParam) {
@@ -5242,4 +5269,324 @@ TEST_F(LeaseCmdsTest, lease6ResendDdnsEnabled) {
     }
 }
 
+// Checks that lease4-del does (or does not) generate an NCR to remove
+// DNS for a given lease based on lease content when DDNS updates are enabled.
+TEST_F(LeaseCmdsTest, lease4DnsRemoveD2Enabled) {
+    // Initialize lease manager (false = v4, true = leases)
+    initLeaseMgr(false, true);
+
+    // Structure detailing a test scenario.
+    struct Scenario {
+        std::string description_;
+        std::string hostname_;
+        bool fqdn_fwd_;
+        bool fqdn_rev_;
+        std::string udpate_ddns_;
+        bool exp_ncr_;
+    };
+
+    bool fwd = true;
+    bool rev = true;
+    bool ncr = true;
+
+    // Three test scenarios to verify each combination of true flags.
+    std::vector<Scenario> scenarios = {
+        {
+            "no_host",
+            "",
+            fwd, rev,
+            "\"update-ddns\": true",
+            !ncr
+        },
+        {
+            "no directions",
+            "myhost.example.com.",
+            !fwd, !rev,
+            "\"update-ddns\": true",
+            !ncr
+        },
+        {
+            "fwd_only",
+            "myhost.example.com.",
+            fwd, !rev,
+            "\"update-ddns\": true",
+            ncr
+        },
+        {
+            "rev_only",
+            "myhost.example.com.",
+            !fwd, rev,
+            "\"update-ddns\": true",
+            ncr
+        },
+        {
+            "both directions",
+            "myhost.example.com.",
+            fwd, rev,
+            "\"update-ddns\": true",
+            ncr
+        },
+        {
+            "default update-ddns",
+            "myhost.example.com.",
+            fwd, rev,
+            "",
+            !ncr
+        },
+        {
+            "update-ddns = false",
+            "myhost.example.com.",
+            fwd, rev,
+            "\"update-ddns\": false",
+            !ncr
+        },
+    };
+
+    for (auto scenario : scenarios) {
+        SCOPED_TRACE(scenario.description_);
+
+        // Let's create a lease with scenario attributes.
+        Lease4Ptr lease = createLease4("192.0.2.8", 44, 0x08, 0x42);
+        lease->hostname_ = scenario.hostname_;
+        lease->fqdn_rev_ = scenario.fqdn_rev_;
+        lease->fqdn_fwd_ = scenario.fqdn_fwd_;
+        ASSERT_TRUE(lmptr_->addLease(lease));
+
+        // NCR Queue should be empty.
+        ASSERT_EQ(ncrQueueSize(), 0);
+
+        // Build the command
+        std::stringstream cmd;
+        cmd <<
+            "{"
+            "    \"command\": \"lease4-del\","
+            "    \"arguments\": {"
+            "        \"ip-address\": \"192.0.2.8\"";
+
+        if (!scenario.udpate_ddns_.empty()) {
+            cmd << "," << scenario.udpate_ddns_;
+        }
+
+        cmd << "}}";
+
+        // Execute the delete command.
+        static_cast<void>(testCommand(cmd.str(), CONTROL_RESULT_SUCCESS, "IPv4 lease deleted."));
+
+        if (!scenario.exp_ncr_) {
+            // Should not have an ncr.
+            ASSERT_EQ(ncrQueueSize(), 0);
+        } else {
+            // We should have an ncr, verify it.
+            ASSERT_EQ(ncrQueueSize(), 1);
+            verifyNameChangeRequest(CHG_REMOVE, scenario.fqdn_rev_, scenario.fqdn_fwd_,
+                                    lease->addr_.toText(), lease->hostname_);
+        }
+
+        // Lease should have been deleted.
+        lease = lmptr_->getLease4(lease->addr_);
+        ASSERT_FALSE(lease);
+    }
+}
+
+// Checks that lease4-del does not generate an NCR to remove
+// DNS for a given lease based on lease content when DDNS
+// updates are disabled.
+TEST_F(LeaseCmdsTest, lease4DnsRemoveD2Disabled) {
+    // Initialize lease manager (false = v4, true = leases)
+    initLeaseMgr(true, true);
+    disableD2();
+
+    // Delete for valid, existing lease.
+    string cmd =
+        "{\n"
+        "    \"command\": \"lease4-del\",\n"
+        "    \"arguments\": {\n"
+        "        \"ip-address\": \"192.0.2.8\",\n"
+        "        \"update-ddns\": true\n"
+        "    }\n"
+        "}";
+
+
+    // Let's create a lease with scenario attributes.
+    Lease4Ptr lease = createLease4("192.0.2.8", 44, 0x08, 0x42);
+    lease->hostname_ = "myhost.example.com.";
+    lease->fqdn_rev_ = true;
+    lease->fqdn_fwd_ = true;
+    ASSERT_TRUE(lmptr_->addLease(lease));
+
+    // NCR Queue is not enabled.
+    ASSERT_EQ(ncrQueueSize(), -1);
+
+    // Execute the delete command.
+    static_cast<void>(testCommand(cmd, CONTROL_RESULT_SUCCESS, "IPv4 lease deleted."));
+
+    // NCR Queue is not enabled.
+    ASSERT_EQ(ncrQueueSize(), -1);
+
+    // Lease should have been deleted.
+    lease = lmptr_->getLease4(lease->addr_);
+    ASSERT_FALSE(lease);
+}
+
+// Checks that lease6-del does (or does not) generate an NCR to remove
+// DNS for a given lease based on lease content when DDNS updates are enabled.
+TEST_F(LeaseCmdsTest, lease6DnsRemoveD2Enabled) {
+    // Initialize lease manager (true = v6, false = no leases)
+    initLeaseMgr(true, true);
+
+    // Structure detailing a test scenario.
+    struct Scenario {
+        std::string description_;
+        std::string hostname_;
+        bool fqdn_fwd_;
+        bool fqdn_rev_;
+        std::string udpate_ddns_;
+        bool exp_ncr_;
+    };
+
+    bool fwd = true;
+    bool rev = true;
+    bool ncr = true;
+
+    // Three test scenarios to verify each combination of true flags.
+    std::vector<Scenario> scenarios = {
+        {
+            "no_host",
+            "",
+            fwd, rev,
+            "\"update-ddns\": true",
+            !ncr
+        },
+        {
+            "no directions",
+            "myhost.example.com.",
+            !fwd, !rev,
+            "\"update-ddns\": true",
+            !ncr
+        },
+        {
+            "fwd_only",
+            "myhost.example.com.",
+            fwd, !rev,
+            "\"update-ddns\": true",
+            ncr
+        },
+        {
+            "rev_only",
+            "myhost.example.com.",
+            !fwd, rev,
+            "\"update-ddns\": true",
+            ncr
+        },
+        {
+            "both directions",
+            "myhost.example.com.",
+            fwd, rev,
+            "\"update-ddns\": true",
+            ncr
+        },
+        {
+            "default update-ddns",
+            "myhost.example.com.",
+            fwd, rev,
+            "",
+            !ncr
+        },
+        {
+            "update-ddns = false",
+            "myhost.example.com.",
+            fwd, rev,
+            "\"update-ddns\": false",
+            !ncr
+        },
+    };
+
+    for (auto scenario : scenarios) {
+        SCOPED_TRACE(scenario.description_);
+
+        // Let's create a lease with scenario attributes.
+        Lease6Ptr lease = createLease6("2001:db8:1::8", 66, 0x77);
+        lease->hostname_ = scenario.hostname_;
+        lease->fqdn_rev_ = scenario.fqdn_rev_;
+        lease->fqdn_fwd_ = scenario.fqdn_fwd_;
+        ASSERT_TRUE(lmptr_->addLease(lease));
+
+        // NCR Queue should be empty.
+        ASSERT_EQ(ncrQueueSize(), 0);
+
+        // Build the command
+        std::stringstream cmd;
+        cmd <<
+            "{"
+            "    \"command\": \"lease6-del\","
+            "    \"arguments\": {"
+            "        \"subnet-id\": 66,\n"
+            "        \"ip-address\": \"2001:db8:1::8\"\n";
+
+        if (!scenario.udpate_ddns_.empty()) {
+            cmd << "," << scenario.udpate_ddns_;
+        }
+
+        cmd << "}}";
+
+        // Execute the delete command.
+        static_cast<void>(testCommand(cmd.str(), CONTROL_RESULT_SUCCESS, "IPv6 lease deleted."));
+
+        if (!scenario.exp_ncr_) {
+            // Should not have an ncr.
+            ASSERT_EQ(ncrQueueSize(), 0);
+        } else {
+            // We should have an ncr, verify it.
+            ASSERT_EQ(ncrQueueSize(), 1);
+            verifyNameChangeRequest(CHG_REMOVE, scenario.fqdn_rev_, scenario.fqdn_fwd_,
+                                    lease->addr_.toText(), lease->hostname_);
+        }
+
+        // Lease should have been deleted.
+        lease = lmptr_->getLease6(Lease::TYPE_NA, lease->addr_);
+        ASSERT_FALSE(lease);
+    }
+}
+
+// Checks that lease6-del does not generate an NCR to remove
+// DNS for a given lease based on lease content when DDNS
+// updates are disabled.
+TEST_F(LeaseCmdsTest, lease6DnsRemoveD2Disabled) {
+    // Initialize lease manager (true = v6, true = leases)
+    initLeaseMgr(true, true);
+    disableD2();
+
+    // Delete for valid, existing lease.
+    string cmd =
+        "{\n"
+        "    \"command\": \"lease6-del\",\n"
+        "    \"arguments\": {\n"
+        "        \"subnet-id\": 66,\n"
+        "        \"ip-address\": \"2001:db8:1::8\",\n"
+        "        \"update-ddns\": true\n"
+        "    }\n"
+        "}";
+
+
+    // Let's create a lease with scenario attributes.
+    Lease6Ptr lease = createLease6("2001:db8:1::8", 66, 0x77);
+    lease->hostname_ = "myhost.example.com.";
+    lease->fqdn_rev_ = true;
+    lease->fqdn_fwd_ = true;
+    ASSERT_TRUE(lmptr_->addLease(lease));
+
+    // NCR Queue is not enabled.
+    ASSERT_EQ(ncrQueueSize(), -1);
+
+    // Execute the delete command.
+    static_cast<void>(testCommand(cmd, CONTROL_RESULT_SUCCESS, "IPv6 lease deleted."));
+
+    // NCR Queue is not enabled.
+    ASSERT_EQ(ncrQueueSize(), -1);
+
+    // Lease should have been deleted.
+    lease = lmptr_->getLease6(Lease::TYPE_NA, lease->addr_);
+    ASSERT_FALSE(lease);
+}
+
 } // end of anonymous namespace
index d3c655ebe4e87c03d1ddebd2d202c38abdb4e568..2dc168572164764f18fb6ca9424ec2276a787cc7 100644 (file)
@@ -1,4 +1,4 @@
-// File created from ../../../src/lib/dhcpsrv/dhcpsrv_messages.mes on Mon Jun 22 2020 17:23
+// File created from ../../../src/lib/dhcpsrv/dhcpsrv_messages.mes on Thu Jul 16 2020 15:23
 
 #include <cstddef>
 #include <log/message_types.h>
index 57a93497f6895595fea7a558e2798553b3a0023f..b11fcdf29ec1cc8bf427f7714d59ca00aa2e48b4 100644 (file)
@@ -1,4 +1,4 @@
-// File created from ../../../src/lib/dhcpsrv/dhcpsrv_messages.mes on Mon Jun 22 2020 17:23
+// File created from ../../../src/lib/dhcpsrv/dhcpsrv_messages.mes on Thu Jul 16 2020 15:23
 
 #ifndef DHCPSRV_MESSAGES_H
 #define DHCPSRV_MESSAGES_H
index 9b8a3afe36ff75399222d1b99f0494b12ae9f684..b977406f6bf12bf3f400fe1af4de793b604b7dfa 100644 (file)
@@ -1479,10 +1479,6 @@ Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
 bool
 Memfile_LeaseMgr::deleteLeaseInternal(const Lease4Ptr& lease) {
     const isc::asiolink::IOAddress& addr = lease->addr_;
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_MEMFILE_DELETE_ADDR)
-        .arg(addr.toText());
-
     Lease4Storage::iterator l = storage4_.find(addr);
     if (l == storage4_.end()) {
         // No such lease
@@ -1518,10 +1514,6 @@ Memfile_LeaseMgr::deleteLease(const Lease4Ptr& lease) {
 bool
 Memfile_LeaseMgr::deleteLeaseInternal(const Lease6Ptr& lease) {
     const isc::asiolink::IOAddress& addr = lease->addr_;
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_MEMFILE_DELETE_ADDR)
-        .arg(addr.toText());
-
     Lease6Storage::iterator l = storage6_.find(addr);
     if (l == storage6_.end()) {
         // No such lease