--- /dev/null
+[func] tmark
+ Lease reclamation is now skipped and rescheduled
+ while DHCP service is disabled. This applies to
+ both kea-dhcp4 and kea-dhcp6.
+ (Gitlab #3693)
const bool remove_lease,
const uint16_t max_unwarned_cycles) {
try {
- server_->alloc_engine_->reclaimExpiredLeases4(max_leases, timeout,
- remove_lease,
- max_unwarned_cycles);
+ if (network_state_->isServiceEnabled()) {
+ server_->alloc_engine_->reclaimExpiredLeases4(max_leases, timeout,
+ remove_lease,
+ max_unwarned_cycles);
+ } else {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_RECLAIM_EXPIRED_LEASES_SKIPPED)
+ .arg(CfgMgr::instance().getCurrentCfg()->
+ getCfgExpiration()->getReclaimTimerWaitTime());
+ }
} catch (const std::exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_RECLAIM_EXPIRED_LEASES_FAIL)
.arg(ex.what());
void
ControlledDhcpv4Srv::deleteExpiredReclaimedLeases(const uint32_t secs) {
- server_->alloc_engine_->deleteExpiredReclaimedLeases4(secs);
+ if (network_state_->isServiceEnabled()) {
+ server_->alloc_engine_->deleteExpiredReclaimedLeases4(secs);
+ }
+
// We're using the ONE_SHOT timer so there is a need to re-schedule it.
TimerMgr::instance()->setup(CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME);
}
extern const isc::log::MessageID DHCP4_QUERY_DATA = "DHCP4_QUERY_DATA";
extern const isc::log::MessageID DHCP4_QUERY_LABEL = "DHCP4_QUERY_LABEL";
extern const isc::log::MessageID DHCP4_RECLAIM_EXPIRED_LEASES_FAIL = "DHCP4_RECLAIM_EXPIRED_LEASES_FAIL";
+extern const isc::log::MessageID DHCP4_RECLAIM_EXPIRED_LEASES_SKIPPED = "DHCP4_RECLAIM_EXPIRED_LEASES_SKIPPED";
extern const isc::log::MessageID DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO = "DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO";
extern const isc::log::MessageID DHCP4_RELEASE = "DHCP4_RELEASE";
extern const isc::log::MessageID DHCP4_RELEASE_DELETED = "DHCP4_RELEASE_DELETED";
"DHCP4_QUERY_DATA", "%1, packet details: %2",
"DHCP4_QUERY_LABEL", "received query: %1",
"DHCP4_RECLAIM_EXPIRED_LEASES_FAIL", "failed to reclaim expired leases: %1",
+ "DHCP4_RECLAIM_EXPIRED_LEASES_SKIPPED", "dhcp6 service is currently disabled. Try again in %1 seconds.",
"DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO", "recovered for query %1 relay agent option from lease %2: %3",
"DHCP4_RELEASE", "%1: address %2 was released properly.",
"DHCP4_RELEASE_DELETED", "%1: address %2 was deleted on release.",
extern const isc::log::MessageID DHCP4_QUERY_DATA;
extern const isc::log::MessageID DHCP4_QUERY_LABEL;
extern const isc::log::MessageID DHCP4_RECLAIM_EXPIRED_LEASES_FAIL;
+extern const isc::log::MessageID DHCP4_RECLAIM_EXPIRED_LEASES_SKIPPED;
extern const isc::log::MessageID DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO;
extern const isc::log::MessageID DHCP4_RELEASE;
extern const isc::log::MessageID DHCP4_RELEASE_DELETED;
This error message indicates that the reclaim expired leases operation failed
and provides the cause of failure.
+% DHCP4_RECLAIM_EXPIRED_LEASES_SKIPPED dhcp6 service is currently disabled. Try again in %1 seconds.
+This debug message is emitted when lease reclamation was scheduled to begin
+but skipped because DHCPv6 service was disabled. Reclamation will continue
+to be scheduled according to the configured value of reclaim-timer-wait-time.
+
% DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO recovered for query %1 relay agent option from lease %2: %3
Logged at debug log level 55.
This debug message indicates that agent options were stashed in the lease for
// stub implementation used in tests.
cb_control_.reset(new TestCBControlDHCPv4());
}
+
+
+ /// @brief Convenience method that enables or disables DHCP service.
+ ///
+ /// @param enable true to enable service, false to disable it.
+ void enableService(bool enable) {
+ if (enable) {
+ network_state_->enableService(NetworkState::USER_COMMAND);
+ } else {
+ network_state_->disableService(NetworkState::USER_COMMAND);
+ }
+ }
};
/// @brief test class for Kea configuration backend.
#endif
+// This test verifies that the DHCP server only reclaims or flushes leases
+// when DHCP6 service is enabled.
+TEST_F(JSONFileBackendTest, reclaimOnlyWhenServiceEnabled) {
+ // This is a basic configuration which enables timers for reclaiming
+ // expired leases and flushing them after 500 seconds since they expire.
+ // Both timers run at 1 second intervals.
+ string config =
+ "{ \"Dhcp4\": {"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ ]"
+ "},"
+ "\"lease-database\": {"
+ " \"type\": \"memfile\","
+ " \"persist\": false"
+ "},"
+ "\"expired-leases-processing\": {"
+ " \"reclaim-timer-wait-time\": 1,"
+ " \"hold-reclaimed-time\": 500,"
+ " \"flush-reclaimed-timer-wait-time\": 1"
+ "},"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, \n"
+ "\"subnet4\": [ ],"
+ "\"valid-lifetime\": 4000 }"
+ "}";
+ writeFile(TEST_FILE, config);
+
+ // Create an instance of the server and initialize it.
+ boost::scoped_ptr<NakedControlledDhcpv4Srv> srv;
+ ASSERT_NO_THROW(srv.reset(new NakedControlledDhcpv4Srv()));
+ ASSERT_NO_THROW(srv->init(TEST_FILE));
+
+ // Create an expired lease. The lease is expired by 40 seconds ago
+ // (valid lifetime = 60, cltt = now - 100). The lease will be reclaimed
+ // but shouldn't be flushed in the database because the reclaimed are
+ // held in the database 500 seconds after reclamation, according to the
+ // current configuration.
+ HWAddrPtr hwaddr_expired(new HWAddr(HWAddr::fromText("00:01:02:03:04:05")));
+ Lease4Ptr lease_expired(new Lease4(IOAddress("10.0.0.1"), hwaddr_expired,
+ ClientIdPtr(), 60,
+ time(NULL) - 100, SubnetID(1)));
+
+ // Create expired-reclaimed lease. The lease has expired 1000 - 60 seconds
+ // ago. It should be removed from the lease database when the "flush" timer
+ // goes off.
+ HWAddrPtr hwaddr_reclaimed(new HWAddr(HWAddr::fromText("01:02:03:04:05:06")));
+ Lease4Ptr lease_reclaimed(new Lease4(IOAddress("10.0.0.2"), hwaddr_reclaimed,
+ ClientIdPtr(), 60,
+ time(NULL) - 1000, SubnetID(1)));
+ lease_reclaimed->state_ = Lease4::STATE_EXPIRED_RECLAIMED;
+
+ // Add leases to the database.
+ LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+ ASSERT_NO_THROW(lease_mgr.addLease(lease_expired));
+ ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed));
+
+ // Make sure they have been added.
+ ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1")));
+ ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2")));
+
+ // Disable service.
+ srv->enableService(false);
+
+ // Poll the timers for a while to make sure that each of them is executed
+ // at least once.
+ ASSERT_NO_THROW(runTimersWithTimeout(srv->getIOService(), 5000));
+
+ // Verify that the leases in the database have not been processed.
+ ASSERT_NO_THROW(
+ lease_expired = lease_mgr.getLease4(IOAddress("10.0.0.1"))
+ );
+ ASSERT_TRUE(lease_expired);
+ ASSERT_EQ(Lease::STATE_DEFAULT, lease_expired->state_);
+
+ // Second lease should not have been removed.
+ ASSERT_NO_THROW(
+ lease_reclaimed = lease_mgr.getLease4(IOAddress("10.0.0.2"))
+ );
+ ASSERT_TRUE(lease_reclaimed);
+ ASSERT_EQ(Lease::STATE_EXPIRED_RECLAIMED, lease_reclaimed->state_);
+
+ // Enable service.
+ srv->enableService(true);
+
+ // Poll the timers for a while to make sure that each of them is executed
+ // at least once.
+ ASSERT_NO_THROW(runTimersWithTimeout(srv->getIOService(), 5000));
+
+ // Verify that the leases in the database have been processed as expected.
+
+ // First lease should be reclaimed, but not removed.
+ ASSERT_NO_THROW(lease_expired = lease_mgr.getLease4(IOAddress("10.0.0.1")));
+ ASSERT_TRUE(lease_expired);
+ EXPECT_TRUE(lease_expired->stateExpiredReclaimed());
+
+ // Second lease should have been removed.
+ ASSERT_NO_THROW(
+ lease_reclaimed = lease_mgr.getLease4(IOAddress("10.0.0.2"));
+ );
+ EXPECT_FALSE(lease_reclaimed);
+}
+
+
} // End of anonymous namespace
const bool remove_lease,
const uint16_t max_unwarned_cycles) {
try {
+ if (network_state_->isServiceEnabled()) {
server_->alloc_engine_->reclaimExpiredLeases6(max_leases, timeout,
remove_lease,
max_unwarned_cycles);
+ } else {
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_RECLAIM_EXPIRED_LEASES_SKIPPED)
+ .arg(CfgMgr::instance().getCurrentCfg()->
+ getCfgExpiration()->getReclaimTimerWaitTime());
+ }
} catch (const std::exception& ex) {
LOG_ERROR(dhcp6_logger, DHCP6_RECLAIM_EXPIRED_LEASES_FAIL)
.arg(ex.what());
void
ControlledDhcpv6Srv::deleteExpiredReclaimedLeases(const uint32_t secs) {
- server_->alloc_engine_->deleteExpiredReclaimedLeases6(secs);
+ if (network_state_->isServiceEnabled()) {
+ server_->alloc_engine_->deleteExpiredReclaimedLeases6(secs);
+ }
+
// We're using the ONE_SHOT timer so there is a need to re-schedule it.
TimerMgr::instance()->setup(CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME);
}
extern const isc::log::MessageID DHCP6_QUERY_LABEL = "DHCP6_QUERY_LABEL";
extern const isc::log::MessageID DHCP6_RAPID_COMMIT = "DHCP6_RAPID_COMMIT";
extern const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_FAIL = "DHCP6_RECLAIM_EXPIRED_LEASES_FAIL";
+extern const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_SKIPPED = "DHCP6_RECLAIM_EXPIRED_LEASES_SKIPPED";
extern const isc::log::MessageID DHCP6_REGISTERED_LEASE_ADD_FAIL = "DHCP6_REGISTERED_LEASE_ADD_FAIL";
extern const isc::log::MessageID DHCP6_REGISTERED_LEASE_UPDATE_FAIL = "DHCP6_REGISTERED_LEASE_UPDATE_FAIL";
extern const isc::log::MessageID DHCP6_RELEASE_NA = "DHCP6_RELEASE_NA";
"DHCP6_QUERY_LABEL", "received query: %1",
"DHCP6_RAPID_COMMIT", "%1: Rapid Commit option received, following 2-way exchange",
"DHCP6_RECLAIM_EXPIRED_LEASES_FAIL", "failed to reclaim expired leases: %1",
+ "DHCP6_RECLAIM_EXPIRED_LEASES_SKIPPED", "dhcp6 service is currently disabled. Try again in %1 seconds.",
"DHCP6_REGISTERED_LEASE_ADD_FAIL", "error in registered lease add for %1",
"DHCP6_REGISTERED_LEASE_UPDATE_FAIL", "error in registered lease update for %1: %2",
"DHCP6_RELEASE_NA", "%1: binding for address %2 and iaid=%3 was released properly",
extern const isc::log::MessageID DHCP6_QUERY_LABEL;
extern const isc::log::MessageID DHCP6_RAPID_COMMIT;
extern const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_FAIL;
+extern const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_SKIPPED;
extern const isc::log::MessageID DHCP6_REGISTERED_LEASE_ADD_FAIL;
extern const isc::log::MessageID DHCP6_REGISTERED_LEASE_UPDATE_FAIL;
extern const isc::log::MessageID DHCP6_RELEASE_NA;
This error message indicates that the reclaim expired leases operation failed
and provides the cause of failure.
+% DHCP6_RECLAIM_EXPIRED_LEASES_SKIPPED dhcp6 service is currently disabled. Try again in %1 seconds.
+This debug message is emitted when lease reclamation was scheduled to begin
+but skipped because DHCPv6 service was disabled. Reclamation will continue
+to be scheduled according to the configured value of reclaim-timer-wait-time.
+
% DHCP6_REGISTERED_LEASE_ADD_FAIL error in registered lease add for %1
This error message indicates that the registered lease add failed and
provides the address being registered.
-// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2025 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// stub implementation used in tests.
cb_control_.reset(new TestCBControlDHCPv6());
}
+
+ /// @brief Convenience method that enables or disables DHCP service.
+ ///
+ /// @param enable true to enable service, false to disable it.
+ void enableService(bool enable) {
+ if (enable) {
+ network_state_->enableService(NetworkState::USER_COMMAND);
+ } else {
+ network_state_->disableService(NetworkState::USER_COMMAND);
+ }
+ }
};
/// @brief test class for Kea configuration backend.
#endif
+// This test verifies that the DHCP server only reclaims or flushes leases
+// when DHCP6 service is enabled.
+TEST_F(JSONFileBackendTest, reclaimOnlyWhenServiceEnabled) {
+ // This is a basic configuration which enables timers for reclaiming
+ // expired leases and flushing them after 500 seconds since they expire.
+ // Both timers run at 1 second intervals.
+ string config =
+ "{ \"Dhcp6\": {"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ ]"
+ "},"
+ "\"lease-database\": {"
+ " \"type\": \"memfile\","
+ " \"persist\": false"
+ "},"
+ "\"expired-leases-processing\": {"
+ " \"reclaim-timer-wait-time\": 1,"
+ " \"hold-reclaimed-time\": 500,"
+ " \"flush-reclaimed-timer-wait-time\": 1"
+ "},"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ ],"
+ "\"preferred-lifetime\": 3000, "
+ "\"valid-lifetime\": 4000 }"
+ "}";
+ writeFile(TEST_FILE, config);
+
+ // Create an instance of the server and initialize it.
+ boost::scoped_ptr<NakedControlledDhcpv6Srv> srv;
+ ASSERT_NO_THROW(srv.reset(new NakedControlledDhcpv6Srv()));
+ ASSERT_NO_THROW(srv->init(TEST_FILE));
+
+ // Create an expired lease. The lease is expired by 40 seconds ago
+ // (valid lifetime = 60, cltt = now - 100). The lease will be reclaimed
+ // but shouldn't be flushed in the database because the reclaimed are
+ // held in the database 500 seconds after reclamation, according to the
+ // current configuration.
+ DuidPtr duid_expired(new DUID(DUID::fromText("00:01:02:03:04:05:06").getDuid()));
+ Lease6Ptr lease_expired(new Lease6(Lease::TYPE_NA, IOAddress("3000::1"),
+ duid_expired, 1, 50, 60, SubnetID(1)));
+ lease_expired->cltt_ = time(NULL) - 100;
+
+ // Create expired-reclaimed lease. The lease has expired 1000 - 60 seconds
+ // ago. It should be removed from the lease database when the "flush" timer
+ // goes off.
+ DuidPtr duid_reclaimed(new DUID(DUID::fromText("01:02:03:04:05:06:07").getDuid()));
+ Lease6Ptr lease_reclaimed(new Lease6(Lease::TYPE_NA, IOAddress("3000::2"),
+ duid_reclaimed, 1, 50, 60, SubnetID(1)));
+ lease_reclaimed->cltt_ = time(NULL) - 1000;
+ lease_reclaimed->state_ = Lease6::STATE_EXPIRED_RECLAIMED;
+
+ // Add leases to the database.
+ LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+ ASSERT_NO_THROW(lease_mgr.addLease(lease_expired));
+ ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed));
+
+ // Make sure they have been added.
+ ASSERT_TRUE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1")));
+ ASSERT_TRUE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2")));
+
+ // Disable service.
+ srv->enableService(false);
+
+ // Poll the timers for a while to make sure that each of them is executed
+ // at least once.
+ ASSERT_NO_THROW(runTimersWithTimeout(srv->getIOService(), 5000));
+
+ // Verify that the leases in the database have not been processed.
+ ASSERT_NO_THROW(
+ lease_expired = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1"))
+ );
+ ASSERT_TRUE(lease_expired);
+ ASSERT_EQ(Lease::STATE_DEFAULT, lease_expired->state_);
+
+ // Second lease should not have been removed.
+ ASSERT_NO_THROW(
+ lease_reclaimed = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2"))
+ );
+ ASSERT_TRUE(lease_reclaimed);
+ ASSERT_EQ(Lease::STATE_EXPIRED_RECLAIMED, lease_reclaimed->state_);
+
+ // Enable service.
+ srv->enableService(true);
+
+ // Poll the timers for a while to make sure that each of them is executed
+ // at least once.
+ ASSERT_NO_THROW(runTimersWithTimeout(srv->getIOService(), 5000));
+
+ // Verify that the leases in the database have been processed as expected.
+
+ // First lease should be reclaimed, but not removed.
+ ASSERT_NO_THROW(
+ lease_expired = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1"))
+ );
+ ASSERT_TRUE(lease_expired);
+ EXPECT_TRUE(lease_expired->stateExpiredReclaimed());
+
+ // Second lease should have been removed.
+ ASSERT_NO_THROW(
+ lease_reclaimed = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2"))
+ );
+ EXPECT_FALSE(lease_reclaimed);
+}
+
} // End of anonymous namespace