libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h
libkea_dhcpsrv_la_SOURCES += network.cc network.h
+libkea_dhcpsrv_la_SOURCES += network_state.cc network_state.h
if HAVE_PGSQL
libkea_dhcpsrv_la_SOURCES += pgsql_connection.cc pgsql_connection.h
--- /dev/null
+// Copyright (C) 2017 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <exceptions/exceptions.h>
+#include <dhcpsrv/network_state.h>
+#include <dhcpsrv/timer_mgr.h>
+#include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Implementation of the @c NetworkState class.
+class NetworkStateImpl : public boost::enable_shared_from_this<NetworkStateImpl> {
+public:
+
+ /// @brief Constructor.
+ NetworkStateImpl()
+ : globally_disabled_(false), disabled_subnets_(), disabled_networks_(),
+ timer_present_(false), timer_mgr_(TimerMgr::instance()) {
+ }
+
+ /// @brief Globally disables or enables DHCP service.
+ void setDisableService(const bool disable) {
+ globally_disabled_ = disable;
+ }
+
+ /// @brief Enables DHCP service globally and per scopes.
+ ///
+ /// If delayed enabling DHCP service has been scheduled, it cancels it.
+ void enableAll() {
+ setDisableService(false);
+
+ /// @todo Enable service for all subnets and networks here.
+
+ destroyTimer();
+ }
+
+ /// @brief Creates a timer couting the time when @c enableAll should be
+ /// automatically called.
+ ///
+ /// If the timer has been already scheduled, it is destroyed and replaced
+ /// with a new timer.
+ ///
+ /// @param seconds Number of seconds to elapse before the @c enableAll is
+ /// called.
+ void createTimer(const unsigned int seconds) {
+ destroyTimer();
+ timer_mgr_->registerTimer("network-state-timer",
+ boost::bind(&NetworkStateImpl::enableAll,
+ shared_from_this()),
+ seconds * 1000,
+ asiolink::IntervalTimer::ONE_SHOT);
+ timer_mgr_->setup("network-state-timer");
+ timer_present_ = true;
+ }
+
+ /// @brief Destroys a timer if present.
+ void destroyTimer() {
+ if (timer_present_) {
+ timer_mgr_->unregisterTimer("network-state-timer");
+ timer_present_ = false;
+ }
+ }
+
+ /// @brief A flag indicating if DHCP service is globally disabled.
+ bool globally_disabled_;
+
+ /// @brief A list of subnets for which the DHCP service has been disabled.
+ NetworkState::Subnets disabled_subnets_;
+
+ /// @brief A list of networks for which the DHCP service has been disabled.
+ NetworkState::Networks disabled_networks_;
+
+ /// @brief Boolean flag indicating if the delayed enabling of the DHCP service
+ /// has been scheduled.
+ bool timer_present_;
+
+ /// @brief A pointer to the common timer manager.
+ ///
+ /// This pointer is held here to make sure that the timer manager is not
+ /// destroyed before an instance of this class is destroyed.
+ TimerMgrPtr timer_mgr_;
+};
+
+NetworkState::NetworkState()
+ : impl_(new NetworkStateImpl()) {
+}
+
+void
+NetworkState::disableService() {
+ impl_->setDisableService(true);
+}
+
+void
+NetworkState::enableService() {
+ impl_->setDisableService(false);
+}
+
+void
+NetworkState::enableAll() {
+ impl_->enableAll();
+}
+
+void
+NetworkState::delayedEnableAll(const unsigned int seconds) {
+ impl_->createTimer(seconds);
+}
+
+bool
+NetworkState::isServiceEnabled() const {
+ return (!impl_->globally_disabled_);
+}
+
+bool
+NetworkState::isDelayedEnableAll() const {
+ return (impl_->timer_present_);
+}
+
+void
+NetworkState::selectiveDisable(const NetworkState::Subnets& subnets) {
+ isc_throw(NotImplemented, "selectiveDisableService is not implemented");
+}
+
+void
+NetworkState::selectiveDisable(const NetworkState::Networks& networks) {
+ isc_throw(NotImplemented, "selectiveDisableService is not implemented");
+}
+
+void
+NetworkState::selectiveEnable(const NetworkState::Subnets& subnets) {
+ isc_throw(NotImplemented, "selectiveEnableService is not implemented");
+}
+
+void
+NetworkState::selectiveEnable(const NetworkState::Networks& networks) {
+ isc_throw(NotImplemented, "selectiveEnableService is not implemented");
+}
+
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 2017 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef NETWORK_STATE_H
+#define NETWORK_STATE_H
+
+#include <cc/data.h>
+#include <dhcpsrv/subnet_id.h>
+#include <boost/shared_ptr.hpp>
+#include <set>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+class NetworkStateImpl;
+
+/// @brief Holds information about service enabling for specific networks.
+///
+/// When the DHCP server receives a command to disable DHCP service entirely
+/// or for specific networks, this has to be recorded to allow for re-enabling
+/// DHCP service for these networks as a result of receiving a command from
+/// the administrator or when the tiemout for re-enabling the service occurs.
+///
+/// In the future, it will be possible to specify "disabled" parameter for
+/// a subnet or network to indicate that this subnet should be excluded from
+/// the service. When a command is subsequently sent to disable a service for
+/// some other subnets and the timeout value is specified, only those subnets
+/// for which a timeout has been specified should be re-enabled. This class
+/// fulfils this requirement by re-enabling only those subnets which have been
+/// temporarily disabled. The subnets specified as "disabled" in the configuration
+/// file should remain disabled until explcitly enabled with a control command.
+///
+/// Disabling DHCP service with a timeout is useful to guard against cases when
+/// the controlling client dies after disabling the DHCP service on the server,
+/// e.g. failover peers may instruct each other to disable the DHCP service while
+/// database synchronization takes place. If the peer subsequently dies, the
+/// surviving server must re-enable DHCP on its own.
+///
+/// @todo This class currently supports only the case of completely disabling
+/// the DHCP service. Disabling per network/subnet will be added later.
+class NetworkState {
+public:
+
+ /// @brief Type of the container holding collection of subnet identifiers.
+ typedef std::set<SubnetID> Subnets;
+
+ /// @brief Type of the container holding collection of shared network names.
+ typedef std::set<std::string> Networks;
+
+ /// @brief Constructor.
+ NetworkState();
+
+ /// @brief Globally disables DHCP service.
+ ///
+ /// The DHCP service becomes disabled for all subnets and networks,
+ /// regardless of the per scope settings.
+ void disableService();
+
+ /// @brief Globally enables DHCP service.
+ ///
+ /// The DHCP service becomes enabled but per scope settings are in effect.
+ /// In order to enable the service for all scopes previously disabled with
+ /// a control command, use @c enableAll.
+ void enableService();
+
+ /// @brief Enables DHCP service globally and for scopes which have been
+ /// disabled as a result of control command.
+ void enableAll();
+
+ /// @brief Schedules enabling DHCP service in the future.
+ ///
+ /// @param seconds Number of seconds after which the service should be enabled
+ /// unless @c enableAll is enabled before that time.
+ void delayedEnableAll(const unsigned int seconds);
+
+ /// @brief Checks if the DHCP service is globally enabled.
+ ///
+ /// @return true if the service is globally enabled, false otherwise.
+ bool isServiceEnabled() const;
+
+ /// @brief Checks if delayed enabling of DHCP services is scheduled.
+ ///
+ /// It indicates that the timer is present which counts the time until
+ /// @c enableAll function will be called automatically.
+ ///
+ /// @return true if delayed enabling of the DHCP service is scheduled,
+ /// false otherwise.
+ bool isDelayedEnableAll() const;
+
+ /// @name Selective disabling/enabling DHCP service per scopes
+ //@{
+
+ /// @brief Disable DHCP service for selected subnets.
+ ///
+ /// @param subnets Collection of subnet identifiers for which the service
+ /// should be disabled.
+ ///
+ /// @throw isc::NotImplemented
+ void selectiveDisable(const NetworkState::Subnets& subnets);
+
+ /// @brief Disable DHCP service for selected networks.
+ ///
+ /// @param networks Collection of shared network names for which the service
+ /// should be disabled.
+ ///
+ /// @throw isc::NotImplemented
+ void selectiveDisable(const NetworkState::Networks& networks);
+
+ /// @brief Enable DHCP service for selected subnets.
+ ///
+ /// @param subnets Collection of subnet identifiers for which the service
+ /// should be disabled.
+ ///
+ /// @throw isc::NotImplemented
+ void selectiveEnable(const NetworkState::Subnets& subnets);
+
+ /// @brief Enable DHCP service for selected networks.
+ ///
+ /// @param networks Collection of shared network names for which the service
+ /// should be enabled.
+ ///
+ /// @throw isc::NotImplemented
+ void selectiveEnable(const NetworkState::Networks& networks);
+
+ //@}
+
+private:
+
+ /// @brief Pointer to the @c NetworkState implementation.
+ boost::shared_ptr<NetworkStateImpl> impl_;
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // NETWORK_STATE_H
libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
libdhcpsrv_unittests_SOURCES += test_utils.cc test_utils.h
libdhcpsrv_unittests_SOURCES += timer_mgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += network_state_unittest.cc
libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
if HAVE_MYSQL
--- /dev/null
+// Copyright (C) 2017 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service.h>
+#include <dhcpsrv/network_state.h>
+#include <dhcpsrv/timer_mgr.h>
+#include <boost/bind.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Test fixture class for @c NetworkState class.
+class NetworkStateTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ NetworkStateTest()
+ : io_service_(new IOService()),
+ test_timer_(*io_service_) {
+ TimerMgr::instance()->unregisterTimers();
+ TimerMgr::instance()->setIOService(io_service_);
+ }
+
+ /// @brief Destructor.
+ virtual ~NetworkStateTest() {
+ // Cancel timers.
+ TimerMgr::instance()->unregisterTimers();
+ test_timer_.cancel();
+ // Make sure IO service will stop when no timers are scheduled.
+ io_service_->stopWork();
+ // Run outstanding tasks.
+ io_service_->run();
+ }
+
+ /// @brief Test timer callback stopping IO service.
+ void testTimerCallback() {
+ TimerMgr::instance()->unregisterTimers();
+ io_service_->stop();
+ }
+
+ /// @brief Runs IO service with a timeout.
+ ///
+ /// @param timeout_ms Timeout for runninr IO service in milliseconds.
+ void runIOService(const long timeout_ms) {
+ test_timer_.setup(boost::bind(&NetworkStateTest::testTimerCallback, this), timeout_ms,
+ IntervalTimer::ONE_SHOT);
+ io_service_->run();
+ }
+
+ /// @brief IO service used during the tests.
+ IOServicePtr io_service_;
+
+ /// @brief Timeout detecting timer.
+ IntervalTimer test_timer_;
+};
+
+// This test verifies that it is possible to disable and then enable service.
+TEST_F(NetworkStateTest, disableEnableService) {
+ NetworkState state;
+ state.disableService();
+ EXPECT_FALSE(state.isServiceEnabled());
+ state.enableService();
+ EXPECT_TRUE(state.isServiceEnabled());
+}
+
+// This test verifies that enableAll() enables the service. This test will be extended
+// in the future to verify that it also enables disabled scopes.
+TEST_F(NetworkStateTest, enableAll) {
+ NetworkState state;
+ state.disableService();
+ EXPECT_FALSE(state.isServiceEnabled());
+ state.enableAll();
+ EXPECT_TRUE(state.isServiceEnabled());
+}
+
+// This test verifies that it is possible to setup delayed execution of enableAll
+// function.
+TEST_F(NetworkStateTest, delayedEnableAll) {
+ NetworkState state;
+ // Disable the service and then schedule enabling it in 1 second.
+ state.disableService();
+ state.delayedEnableAll(1);
+ // Initially the service should be still disabled.
+ EXPECT_FALSE(state.isServiceEnabled());
+ // After running IO service for 2 seconds, the service should be enabled.
+ runIOService(2000);
+ EXPECT_TRUE(state.isServiceEnabled());
+}
+
+// This test verifies that explicitly enabling the service cancels the timer
+// scheduled for automatically enabling it.
+TEST_F(NetworkStateTest, earlyEnableAll) {
+ NetworkState state;
+ // Disable the service.
+ state.disableService();
+ EXPECT_FALSE(state.isServiceEnabled());
+ // Schedule enabling the service in 2 seconds.
+ state.delayedEnableAll(2);
+ // Explicitly enable the service.
+ state.enableAll();
+ // The timer should be now canceled and the service should be enabled.
+ EXPECT_FALSE(state.isDelayedEnableAll());
+ EXPECT_TRUE(state.isServiceEnabled());
+}
+
+// This test verifies that it is possible to call delayedEnableAll multiple times
+// and that it results in only one timer being scheduled.
+TEST_F(NetworkStateTest, multipleDelayedEnableAll) {
+ NetworkState state;
+ // Disable the service and then schedule enabling it in 1 second.
+ state.disableService();
+ // Schedule the first timer for 5 seconds.
+ state.delayedEnableAll(5);
+ // When calling it the second time the old timer should be destroyed and
+ // the timeout should be set to 2 seconds.
+ state.delayedEnableAll(2);
+ // Initially the service should be still disabled.
+ EXPECT_FALSE(state.isServiceEnabled());
+ // After running IO service for 3 seconds, the service should be enabled.
+ runIOService(3000);
+ EXPECT_TRUE(state.isServiceEnabled());
+ // The timer should not be present, even though the first timer was created
+ // with 5 seconds interval.
+ EXPECT_FALSE(state.isDelayedEnableAll());
+}
+
+
+} // end of anonymous namespace