Parcourir la source

[2615] v4 clients may now release out-of-range leases

    Modified v4 server to not enforce subnet match when clients attempt
    to release an existing, but out-of-range lease.

    Added a battery of unit tests to verify proper v4 server behavior
    when clients attempt to renew or release out-of-range addresses.

src/bin/dhcp4/dhcp4_messages.mes
 - deleted DHCP4_RELEAE_FILE_NO SUBNET as it is now obsolete

src/bin/dhcp4/dhcp4_srv.cc
 -  Dhcpv4Srv::processRelease() - removed the check to make sure the
    release pertains to a configured subnet.  This allows clients to
    release leases after configuration changes rendered them out of
    range.

src/bin/dhcp4/tests/Makefile.am
  - added out_of_range_unittest.cc

src/bin/dhcp4/tests/out_of_range_unittest.cc
 - New file containing DHCPv4 tests for server behavior regarding
   renews and releases of "out of range" addresses.

src/bin/dhcp4/tests/release_unittest.cc
 - TEST_F(ReleaseTest, releaseNoSubnet) - modified the test to
   verify that a client CAN release an out-of-range lease
Thomas Markwalder il y a 9 ans
Parent
commit
6bffb344d3

+ 0 - 6
src/bin/dhcp4/dhcp4_messages.mes

@@ -541,12 +541,6 @@ the client and transaction identification information. The second
 argument contains the IPv4 address which the client is trying to
 release.
 
-% DHCP4_RELEASE_FAIL_NO_SUBNET %1: client is trying to release a lease %2 from a subnet which cannot be selected.
-This warning message indicates that client tried to release an address
-from an improper location.  The first argument contains the client and
-transaction identification information. The second argument contains
-the address which the client is trying to release.
-
 % DHCP4_RELEASE_FAIL_WRONG_CLIENT %1: client is trying to release the lease %2 which belongs to a different client
 This debug message is issued when a client is trying to release the
 lease for the address which is currently used by another client, i.e.

+ 0 - 10
src/bin/dhcp4/dhcp4_srv.cc

@@ -1738,16 +1738,6 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
         client_id = ClientIdPtr(new ClientId(opt->getData()));
     }
 
-    Subnet4Ptr subnet = selectSubnet(release);
-    if (!subnet) {
-        // No subnet - release no sent from the proper location
-        LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL,
-                  DHCP4_RELEASE_FAIL_NO_SUBNET)
-            .arg(release->getLabel())
-            .arg(release->getCiaddr().toText());
-        return;
-    }
-
     try {
         // Do we have a lease for that particular address?
         Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getCiaddr());

+ 1 - 0
src/bin/dhcp4/tests/Makefile.am

@@ -86,6 +86,7 @@ dhcp4_unittests_SOURCES += dhcp4_client.cc dhcp4_client.h
 dhcp4_unittests_SOURCES += inform_unittest.cc
 dhcp4_unittests_SOURCES += dora_unittest.cc
 dhcp4_unittests_SOURCES += release_unittest.cc
+dhcp4_unittests_SOURCES += out_of_range_unittest.cc
 
 dhcp4_unittests_SOURCES += kea_controller_unittest.cc
 

+ 538 - 0
src/bin/dhcp4/tests/out_of_range_unittest.cc

@@ -0,0 +1,538 @@
+// Copyright (C) 2015 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 <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp_ddns/ncr_msg.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/host.h>
+#include <dhcpsrv/host_mgr.h>
+#include <dhcpsrv/subnet_id.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <dhcp4/tests/dhcp4_client.h>
+#include <boost/shared_ptr.hpp>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @brief Set of JSON configurations used throughout the out of range
+///  tests.
+///
+/// - Configuration 0 - Reference configuration, all tests start with the
+///   server configured with this configuration:
+///   - 1 subnet: 10.0.0.0/24
+///   - with one pool  10.0.0.10 - 10.0.0.100
+///   - 1 address reservation (fixed host) for HW address:
+///     ff:ff:ff:ff:ff:01
+///   - 1 hostname reservation (dynamic host) for HW address:
+///     dd:dd:dd:dd:dd:01,
+///   - DDNS enabled
+/// - Configuration 1:
+///   - Same as configuration 1 but with a different pool range
+/// - Configuration 2 - same subnet as reference, different pool,
+///       no reservations
+/// - Configuration 3 - different subnet with reservations
+/// - Configuration 4 - different subnet with no reservations
+/// - Configuration 5 - same as reference, no reservations
+///
+const char* OOR_CONFIGS[] = {
+// Configuration 0 - reference configuration
+    "{ \"interfaces-config\": {"
+        "      \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\", "
+        "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
+        "    \"reservations\": [ "
+        "       {"
+        "         \"hw-address\": \"ff:ff:ff:ff:ff:01\","
+        "         \"ip-address\": \"10.0.0.7\""
+        "       },"
+        "       {"
+        "         \"hw-address\": \"dd:dd:dd:dd:dd:01\","
+        "         \"hostname\": \"reserved.example.com\""
+        "       }"
+        "    ]"
+        "} ],"
+        "\"dhcp-ddns\": {"
+        "     \"enable-updates\": true,"
+        "     \"qualifying-suffix\": \"\""
+        "}"
+    "}",
+
+// Configuration 1 - same subnet as reference, different pool
+    "{ \"interfaces-config\": {"
+        "      \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\", "
+        "    \"pools\": [ { \"pool\": \"10.0.0.101-10.0.0.200\" } ],"
+        "    \"reservations\": [ "
+        "       {"
+        "         \"hw-address\": \"ff:ff:ff:ff:ff:01\","
+        "         \"ip-address\": \"10.0.0.7\""
+        "       },"
+        "       {"
+        "         \"hw-address\": \"dd:dd:dd:dd:dd:01\","
+        "         \"hostname\": \"reserved.example.com\""
+        "       }"
+        "    ]"
+        "} ],"
+        "\"dhcp-ddns\": {"
+        "     \"enable-updates\": true,"
+        "     \"qualifying-suffix\": \"\""
+        "}"
+    "}",
+
+// Configuration 2 - same subnet as reference, different pool, no reservations
+    "{ \"interfaces-config\": {"
+        "      \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\", "
+        "    \"pools\": [ { \"pool\": \"10.0.0.101-10.0.0.200\" } ],"
+        "} ],"
+        "\"dhcp-ddns\": {"
+        "     \"enable-updates\": true,"
+        "     \"qualifying-suffix\": \"\""
+        "}"
+    "}",
+
+
+// Configuration 3 - different subnet with reservations
+    "{ \"interfaces-config\": {"
+        "      \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"pools\": [ { \"pool\": \"192.0.2.101-192.0.2.200\" } ],"
+        "    \"reservations\": [ "
+        "       {"
+        "         \"hw-address\": \"ff:ff:ff:ff:ff:01\","
+        "         \"ip-address\": \"192.0.2.7\""
+        "       },"
+        "       {"
+        "         \"hw-address\": \"dd:dd:dd:dd:dd:01\","
+        "         \"hostname\": \"reserved.example.com\""
+        "       }"
+        "    ]"
+        "} ],"
+        "\"dhcp-ddns\": {"
+        "     \"enable-updates\": true,"
+        "     \"qualifying-suffix\": \"\""
+        "}"
+    "}",
+
+// Configuration 4 - different subnet with no reservations
+    "{ \"interfaces-config\": {"
+        "      \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"pools\": [ { \"pool\": \"192.0.2.101-192.0.2.200\" } ]"
+        "} ],"
+        "\"dhcp-ddns\": {"
+        "     \"enable-updates\": true,"
+        "     \"qualifying-suffix\": \"\""
+        "}"
+    "}",
+
+// Configuration 5 - same as reference, no reservations
+    "{ \"interfaces-config\": {"
+        "      \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\", "
+        "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
+        "} ],"
+        "\"dhcp-ddns\": {"
+        "     \"enable-updates\": true,"
+        "     \"qualifying-suffix\": \"\""
+        "}"
+    "}",
+
+};
+
+/// @brief Enum for indexing into the array of configurations.
+/// These were created to make the test cases easier to follow.
+enum CfgIndex {
+    REF_CFG = 0,
+    DIFF_POOL,
+    DIFF_POOL_NO_HR,
+    DIFF_SUBNET,
+    DIFF_SUBNET_NO_HR,
+    NO_HR = 4
+};
+
+/// @brief Enum for specifying expected response to client renewal attempt
+enum RenewOutcome {
+    DOES_RENEW,
+    DOES_NOT_RENEW
+};
+
+/// @brief Enum for specifying expected response to client release attempt
+enum ReleaseOutcome {
+    DOES_RELEASE,
+    DOES_NOT_RELEASE
+};
+
+/// @brief Test fixture class for testing various exchanges when the client's
+/// leased address is out of range due to configuration changes.
+class OutOfRangeTest : public Dhcpv4SrvTest {
+public:
+    D2ClientMgr& d2_mgr_;
+
+    /// @brief Constructor.
+    ///
+    /// Sets up fake interfaces.
+    OutOfRangeTest()
+        : Dhcpv4SrvTest(),
+          d2_mgr_(CfgMgr::instance().getD2ClientMgr()),
+          iface_mgr_test_config_(true) {
+        IfaceMgr::instance().openSockets4();
+    }
+
+    /// @brief Desctructor.
+    ///
+    /// Cleans up statistics after the test.
+    ~OutOfRangeTest() {
+    }
+
+    void configure(const std::string& config, Dhcp4Client& client) {
+        NakedDhcpv4Srv& server = *client.getServer();
+        ASSERT_NO_FATAL_FAILURE(Dhcpv4SrvTest::configure(config, server));
+        if (d2_mgr_.ddnsEnabled()) {
+            ASSERT_NO_THROW(server.startD2());
+        }
+    }
+
+    /// @briefVerify that a NameChangeRequest has been queued
+    ///
+    /// Checks that a NCR of a given type (ADD or REMOVE) for a given
+    /// ip address has been queued.  If the NCR exits, it is processed
+    /// off the queue.  Note the function expects there to be 1 and only
+    /// 1 NCR queued.
+    ///
+    /// @param type - NCR type exepcted, either CHG_ADD or CHG_REMOVE
+    /// @param addr - string containing the ip address expected in the NCR
+    void verifyNameChangeRequest(const isc::dhcp_ddns::NameChangeType type,
+                                 const std::string& addr) {
+        ASSERT_EQ(1, d2_mgr_.getQueueSize());
+
+        isc::dhcp_ddns::NameChangeRequestPtr ncr;
+        ASSERT_NO_THROW(ncr = d2_mgr_.peekAt(0));
+        ASSERT_TRUE(ncr);
+
+        EXPECT_EQ(type, ncr->getChangeType());
+        EXPECT_EQ(addr, ncr->getIpAddress());
+
+        // Process the message off the queue
+        ASSERT_NO_THROW(d2_mgr_.runReadyIO());
+        ASSERT_EQ(0, d2_mgr_.getQueueSize());
+    }
+
+    /// @brief Conducts a single out-of-range test scenario
+    ///
+    /// Each test cycles consists of a the following two stages, the first is
+    /// a set-up stage during which the server is configured with an initial,
+    /// reference, configuration and a client then verifies that it can aquire
+    /// and renew a lease.  The second stage verifies that the server, having
+    /// been reconfigured such that the original lease is now "out-of-range",
+    /// responds correctly to the same client first attempting to renew the
+    /// lease and then attempting to release the lease.
+    ///
+    /// The test also expects the configuration to enable DDNS, and verifies
+    /// that a DNS add is done during stage one, and a DNS remove is done
+    /// in stage two as part of the release request processing.
+    ///
+    /// @param cfg_idx - index of the "replacement" configuration used to
+    /// reconfigure the server during stage two
+    /// @param hwaddress - text value, if not empty, to use as the hardware
+    /// address.  This is used for host reservation tests.
+    /// in client queries
+    /// @param expected_address - text value of the expected address.  If not
+    /// empty the test will fail if the server responds with a different lease
+    /// address
+    /// @param renew_outcome - expected server reaction in response to the
+    /// client's stage two renewal attempt.
+    /// @param release_outcome - expected server reaction in response to the
+    /// client's stage two release attempt.  Currently defaults to DOES_RELEASE
+    /// as no cases have been identified which do otherwise.
+    void oorRenewReleaseTest(enum CfgIndex cfg_idx,
+                             const std::string& hwaddress,
+                             const std::string& expected_address,
+                             enum RenewOutcome renew_outcome,
+                             enum ReleaseOutcome release_outcome = DOES_RELEASE);
+
+    /// @brief Interface Manager's fake configuration control.
+    IfaceMgrTestConfig iface_mgr_test_config_;
+
+};
+
+void
+OutOfRangeTest::oorRenewReleaseTest(enum CfgIndex cfg_idx,
+                             const std::string& hwaddress,
+                             const std::string& expected_address,
+                             enum RenewOutcome renew_outcome,
+                             enum ReleaseOutcome release_outcome) {
+    // STAGE ONE:
+
+    // Step 1 is to acquire the lease
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Configure DHCP server.
+    configure(OOR_CONFIGS[REF_CFG], client);
+
+    // Set the host name so DNS updates will be performed
+    client.includeHostname("test.example.com");
+
+    // Set the hw address if given one
+    if (!hwaddress.empty()) {
+        client.setHWAddress(hwaddress);
+    }
+
+    // Aquire the lease via DORA
+    ASSERT_NO_THROW(client.doDORA());
+
+    // Make sure that the server responded.
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+
+    // Verify a lease was created
+    IOAddress leased_address = client.config_.lease_.addr_;
+    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(leased_address);
+    ASSERT_TRUE(lease);
+
+    // Check the expected address if given
+    if (!expected_address.empty()) {
+        ASSERT_EQ(leased_address, expected_address);
+    }
+
+    // Verify that a DNS add was requested
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, leased_address.toText());
+
+    // The address is still valid, let's verify the lease can be renewed
+    // Set the unicast destination address to indicate that it is a renewal.
+    client.setState(Dhcp4Client::RENEWING);
+    client.setDestAddress(IOAddress("10.0.0.1"));
+    ASSERT_NO_THROW(client.doRequest());
+
+    // Verify that we received an ACK to our renewal
+    resp = client.getContext().response_;
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+
+    // STAGE TWO:
+
+    // Now reconfigure which should render our leased address out-of-range
+    configure(OOR_CONFIGS[cfg_idx], client);
+
+    // Try to renew after the configuration change..
+    ASSERT_NO_THROW(client.doRequest());
+    resp = client.getContext().response_;
+
+    // Verify we got ACK'd or NAK'd as expected
+    ASSERT_EQ((renew_outcome == DOES_RENEW ? DHCPACK : DHCPNAK),
+              static_cast<int>(resp->getType()));
+
+    // Verify that the lease still exists in the database as it has not
+    // been explicitly released.
+    lease = LeaseMgrFactory::instance().getLease4(leased_address);
+    ASSERT_TRUE(lease);
+
+    // Recreate the client lease info, a preceding NAK will have wiped it out
+    client.createLease(leased_address, lease->valid_lft_);
+
+    // Send the release. The server should remove it from the DB and
+    // request DNS remove.
+    ASSERT_NO_THROW(client.doRelease());
+
+    lease = LeaseMgrFactory::instance().getLease4(leased_address);
+
+    if (release_outcome == DOES_RELEASE) {
+        EXPECT_FALSE(lease);
+        // Verify the DNS remove was queued.
+        verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
+                                leased_address.toText());
+    } else {
+        // Lease should still exist, and no NCR should be queued.
+        EXPECT_TRUE(lease);
+        EXPECT_EQ(0, d2_mgr_.getQueueSize());
+    }
+}
+
+
+// Verifies that once-valid lease, whose address is no longer
+// within the subnet's pool:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicOutOfPool) {
+
+    std::string hwaddress = "";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address,
+                        DOES_NOT_RENEW);
+
+}
+
+// Verifies that once-valid lease whose address is no longer
+// within any configured subnet:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicOutOfSubnet) {
+
+    std::string hwaddress = "";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address,
+                        DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid dynamic address host reserveration,
+// whose address is no longer within the subnet's pool:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicHostOutOfPool) {
+    std::string hwaddress = "dd:dd:dd:dd:dd:01";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address, DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid dynamic address host reserveration,
+// whose address is no longer within any configured subnet:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicHostOutOfSubnet) {
+    std::string hwaddress = "dd:dd:dd:dd:dd:01";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address,
+                        DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid dynamic address host reserveration,
+// whose address is within the configured subnet, but whose
+// reservation has been removed:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicHostReservationRemoved) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    std::string hwaddress = "dd:dd:dd:dd:dd:01";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(NO_HR, hwaddress, expected_address, DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid dynamic address host reserveration,
+// whose address is no longer within any configured subnet, and which
+// no longer has reservation defined:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicHostOutOfSubnetReservationRemoved) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    std::string hwaddress = "dd:dd:dd:dd:dd:01";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_SUBNET_NO_HR, hwaddress, expected_address,
+                        DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid in-subnet fixed-address host reserveration,
+// after the subnet pool changes:
+//
+// a: Is NAK'd upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, fixedHostOutOfSubnet) {
+    std::string hwaddress = "ff:ff:ff:ff:ff:01";
+    std::string expected_address = "10.0.0.7";
+
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address,
+                        DOES_NOT_RENEW);
+}
+
+
+// Test verifies that once-valid in-subnet fixed-address host reserveration,
+// after the subnet pool is changed:
+//
+// a: Is ACK'd upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, fixedHostDifferentPool) {
+    std::string hwaddress = "ff:ff:ff:ff:ff:01";
+    std::string expected_address = "10.0.0.7";
+
+    oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address, DOES_RENEW);
+}
+
+// Test verifies that once-valid in-subnet fixed-address host reserveration,
+// whose reservation has been removed from the configuration
+//
+// a: Is NAK'd upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, fixedHostReservationRemoved) {
+    std::string hwaddress = "ff:ff:ff:ff:ff:01";
+    std::string expected_address = "10.0.0.7";
+
+    oorRenewReleaseTest(NO_HR, hwaddress, expected_address, DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid fixed address host reserveration,
+// whose address is no longer within any configured subnet
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is released properly upon release, including DNS removal
+//
+TEST_F(OutOfRangeTest, fixedHostOutOfSubnetReservationRemoved) {
+    std::string hwaddress = "ff:ff:ff:ff:ff:01";
+    std::string expected_address = "10.0.0.7";
+
+    oorRenewReleaseTest(DIFF_SUBNET_NO_HR, hwaddress, expected_address,
+                        DOES_NOT_RENEW);
+}
+
+} // end of anonymous namespace

+ 4 - 4
src/bin/dhcp4/tests/release_unittest.cc

@@ -285,8 +285,8 @@ TEST_F(ReleaseTest, releaseNonMatchingIPAddress) {
     ASSERT_TRUE(lease);
 }
 
-// This test verifies that incoming RELEASE from a bad location
-// is correctly dropped.
+// This test verifies that an incoming RELEASE for an address within
+// a subnet that has been removed
 TEST_F(ReleaseTest, releaseNoSubnet) {
     Dhcp4Client client(Dhcp4Client::SELECTING);
     // Configure DHCP server.
@@ -303,9 +303,9 @@ TEST_F(ReleaseTest, releaseNoSubnet) {
     // Send the release
     ASSERT_NO_THROW(client.doRelease());
 
-    // Check that the lease was not removed (due to no subnet)
+    // Check that the lease was removed
     Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(leased_address);
-    EXPECT_TRUE(lease);
+    EXPECT_FALSE(lease);
 }
 
 } // end of anonymous namespace