Commit 36dc68ff authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac5442'

parents 58c62f07 6bb0172d
......@@ -557,6 +557,42 @@ $ curl -X POST -H "Content-Type: application/json" -d '{ "command": "config-get"
</para>
</section> <!-- end of command-shutdown -->
<section id="command-dhcp-disable">
<title>dhcp-disable</title>
<para>
The <emphasis>dhcp-disable</emphasis> command globally disables the DHCP
service. The server continues to operate, but it drops all received DHCP
messages. This command is useful when the server's maintenance requires that
the server temporarily stops allocating new leases and renew existing leases.
It is also useful in failover like configurations during a synchronization of
the lease databases at startup or recovery after a failure. The optional parameter
<emphasis>max-period</emphasis> specifies the time in seconds after which the
DHCP service should be automatically re-enabled if the
<emphasis>dhcp-enable</emphasis> command is not sent before this time elapses.
</para>
<screen>
{
"command": "dhcp-disable",
"arguments": {
"max-period": 20
}
}
</screen>
</section> <!-- end of command-dhcp-disable -->
<section id="command-dhcp-enable">
<title>dhcp-enable</title>
<para>
The <emphasis>dhcp-enable</emphasis> command globally enables the DHCP
service.
</para>
<screen>
{
"command": "dhcp-enable"
}
</screen>
</section> <!-- end of command-dhcp-disable -->
<section id="command-version-get">
<title>version-get</title>
<para>
......
......@@ -4556,6 +4556,8 @@ autogenerated IDs are not stable across configuration changes.</para>
<listitem>config-set</listitem>
<listitem>config-test</listitem>
<listitem>config-write</listitem>
<listitem>dhcp-disable</listitem>
<listitem>dhcp-enable</listitem>
<listitem>leases-reclaim</listitem>
<listitem>list-commands</listitem>
<listitem>shutdown</listitem>
......
......@@ -4558,6 +4558,8 @@ autogenerated IDs are not stable across configuration changes.
<listitem>config-set</listitem>
<listitem>config-test</listitem>
<listitem>config-write</listitem>
<listitem>dhcp-disable</listitem>
<listitem>dhcp-enable</listitem>
<listitem>leases-reclaim</listitem>
<listitem>list-commands</listitem>
<listitem>shutdown</listitem>
......
......@@ -19,6 +19,7 @@
#include <stats/stats_mgr.h>
#include <cfgrpt/config_report.h>
#include <signal.h>
#include <sstream>
using namespace isc::data;
using namespace isc::dhcp;
......@@ -374,6 +375,64 @@ ControlledDhcpv4Srv::commandConfigTestHandler(const string&,
return (checkConfig(dhcp4));
}
ConstElementPtr
ControlledDhcpv4Srv::commandDhcpDisableHandler(const std::string&,
ConstElementPtr args) {
std::ostringstream message;
int64_t max_period = 0;
// Parse arguments to see if the 'max-period' parameter has been specified.
if (args) {
// Arguments must be a map.
if (args->getType() != Element::map) {
message << "arguments for the 'dhcp-disable' command must be a map";
} else {
ConstElementPtr max_period_element = args->get("max-period");
// max-period is optional.
if (max_period_element) {
// It must be an integer, if specified.
if (max_period_element->getType() != Element::integer) {
message << "'max-period' argument must be a number";
} else {
// It must be positive integer.
max_period = max_period_element->intValue();
if (max_period <= 0) {
message << "'max-period' must be positive integer";
}
// The user specified that the DHCP service should resume not
// later than in max-period seconds. If the 'dhcp-enable' command
// is not sent, the DHCP service will resume automatically.
network_state_.delayedEnableAll(static_cast<unsigned>(max_period));
}
}
}
}
// No error occurred, so let's disable the service.
if (message.tellp() == 0) {
network_state_.disableService();
message << "DHCPv4 service disabled";
if (max_period > 0) {
message << " for " << max_period << " seconds";
}
// Success.
return (config::createAnswer(CONTROL_RESULT_SUCCESS, message.str()));
}
// Failure.
return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
}
ConstElementPtr
ControlledDhcpv4Srv::commandDhcpEnableHandler(const std::string&, ConstElementPtr) {
network_state_.enableService();
return (config::createAnswer(CONTROL_RESULT_SUCCESS, "DHCP service successfully enabled"));
}
ConstElementPtr
ControlledDhcpv4Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
ElementPtr extended = Element::create(Dhcpv4Srv::getVersion(true));
......@@ -455,6 +514,12 @@ ControlledDhcpv4Srv::processCommand(const string& command,
} else if (command == "config-test") {
return (srv->commandConfigTestHandler(command, args));
} else if (command == "dhcp-disable") {
return (srv->commandDhcpDisableHandler(command, args));
} else if (command == "dhcp-enable") {
return (srv->commandDhcpEnableHandler(command, args));
} else if (command == "version-get") {
return (srv->commandVersionGetHandler(command, args));
......@@ -619,6 +684,12 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
CommandMgr::instance().registerCommand("config-write",
boost::bind(&ControlledDhcpv4Srv::commandConfigWriteHandler, this, _1, _2));
CommandMgr::instance().registerCommand("dhcp-enable",
boost::bind(&ControlledDhcpv4Srv::commandDhcpEnableHandler, this, _1, _2));
CommandMgr::instance().registerCommand("dhcp-disable",
boost::bind(&ControlledDhcpv4Srv::commandDhcpDisableHandler, this, _1, _2));
CommandMgr::instance().registerCommand("libreload",
boost::bind(&ControlledDhcpv4Srv::commandLibReloadHandler, this, _1, _2));
......@@ -675,6 +746,8 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
CommandMgr::instance().deregisterCommand("leases-reclaim");
CommandMgr::instance().deregisterCommand("libreload");
CommandMgr::instance().deregisterCommand("config-set");
CommandMgr::instance().deregisterCommand("dhcp-disable");
CommandMgr::instance().deregisterCommand("dhcp-enable");
CommandMgr::instance().deregisterCommand("shutdown");
CommandMgr::instance().deregisterCommand("statistic-get");
CommandMgr::instance().deregisterCommand("statistic-get-all");
......
......@@ -223,12 +223,34 @@ private:
commandConfigTestHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief A handler for processing 'dhcp-disable' command.
///
/// @param command command name (ignored).
/// @param args aguments for the command. It must be a map and
/// it may include optional 'max-period' parameter.
///
/// @return result of the command.
isc::data::ConstElementPtr
commandDhcpDisableHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief A handler for processing 'dhcp-enable' command.
///
/// @param command command name (ignored)
/// @param args arguments for the command (ignored).
///
/// @return result of the command.
isc::data::ConstElementPtr
commandDhcpEnableHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @Brief handler for processing 'version-get' command
///
/// This handler processes version-get command, which returns
/// over the control channel the -v and -V command line arguments.
/// @param command (parameter ignored)
/// @param args (parameter ignored)
/// @param args (parameter ignored)
///
/// @return status of the command with the version in text and
/// the extended version in arguments.
......@@ -241,7 +263,7 @@ private:
/// This handler processes build-report command, which returns
/// over the control channel the -W command line argument.
/// @param command (parameter ignored)
/// @param args (parameter ignored)
/// @param args (parameter ignored)
///
/// @return status of the command with the config report
isc::data::ConstElementPtr
......
......@@ -404,6 +404,12 @@ will not send a response but will instead ignore the packet. The first
argument contains the client and transaction identification information.
The second argument includes the details of the error.
% DHCP4_PACKET_DROP_0008 %1: DHCP service is globally disabled
This debug message is issued when a packet is dropped because the DHCP service
has been temporarily disabled. This affects all received DHCP packets. The
service may be enabled by the "dhcp-enable" control command or automatically
after a specified amount of time since receiving "dhcp-disable" command.
% DHCP4_PACKET_NAK_0001 %1: failed to select a subnet for incoming packet, src %2, type %3
This error message is output when a packet was received from a subnet
for which the DHCPv4 server has not been configured. The most probable
......
......@@ -416,7 +416,7 @@ const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast,
const bool direct_response_desired)
: io_service_(new IOService()), shutdown_(true), alloc_engine_(), port_(port),
use_bcast_(use_bcast) {
use_bcast_(use_bcast), network_state_(NetworkState::DHCPv4) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
try {
......@@ -804,7 +804,14 @@ Dhcpv4Srv::run_one() {
return;
}
processPacket(query, rsp);
// If the DHCP service has been globally disabled, drop the packet.
if (!network_state_.isServiceEnabled()) {
LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_BASIC,
DHCP4_PACKET_DROP_0008)
.arg(query->getLabel());
} else {
processPacket(query, rsp);
}
if (!rsp) {
return;
......
......@@ -15,10 +15,11 @@
#include <dhcp/option4_client_fqdn.h>
#include <dhcp/option_custom.h>
#include <dhcp_ddns/ncr_msg.h>
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/network_state.h>
#include <dhcpsrv/subnet.h>
#include <hooks/callout_handle.h>
#include <dhcpsrv/daemon.h>
......@@ -840,6 +841,12 @@ private:
uint16_t port_; ///< UDP port number on which server listens.
bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
protected:
/// @brief Holds information about disabled DHCP service and/or
/// disabled subnet/network scopes.
NetworkState network_state_;
public:
/// Class methods for DHCPv4-over-DHCPv6 handler
......
......@@ -86,6 +86,7 @@ public:
/// Expose internal methods for the sake of testing
using Dhcpv4Srv::receivePacket;
using Dhcpv4Srv::network_state_;
};
/// @brief Default control connection timeout.
......@@ -1154,6 +1155,74 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configReloadValid) {
::remove("test8.json");
}
// This test verifies if it is possible to disable DHCP service via command.
TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisable) {
createUnixChannelServer();
std::string response;
sendUnixCommand("{ \"command\": \"dhcp-disable\" }", response);
ConstElementPtr rsp;
// The response should be a valid JSON.
EXPECT_NO_THROW(rsp = Element::fromJSON(response));
ASSERT_TRUE(rsp);
int status;
ConstElementPtr cfg = parseAnswer(status, rsp);
EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
EXPECT_FALSE(server_->network_state_.isServiceEnabled());
}
// This test verifies that it is possible to disable DHCP service for a short
// period of time, after which the service is automatically enabled.
TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisableTemporarily) {
createUnixChannelServer();
std::string response;
// Send a command to disable DHCP service for 3 seconds.
sendUnixCommand("{"
" \"command\": \"dhcp-disable\","
" \"arguments\": {"
" \"max-period\": 3"
" }"
"}", response);
ConstElementPtr rsp;
// The response should be a valid JSON.
EXPECT_NO_THROW(rsp = Element::fromJSON(response));
ASSERT_TRUE(rsp);
int status;
ConstElementPtr cfg = parseAnswer(status, rsp);
EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
// The service should be disabled.
EXPECT_FALSE(server_->network_state_.isServiceEnabled());
// And the timer should be scheduled which counts the time to automatic
// enabling of the service.
EXPECT_TRUE(server_->network_state_.isDelayedEnableAll());
}
// This test verifies if it is possible to enable DHCP service via command.
TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnable) {
createUnixChannelServer();
std::string response;
sendUnixCommand("{ \"command\": \"dhcp-enable\" }", response);
ConstElementPtr rsp;
// The response should be a valid JSON.
EXPECT_NO_THROW(rsp = Element::fromJSON(response));
ASSERT_TRUE(rsp);
int status;
ConstElementPtr cfg = parseAnswer(status, rsp);
EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
EXPECT_TRUE(server_->network_state_.isServiceEnabled());
}
/// Verify that concurrent connections over the control channel can be
/// established.
/// @todo Future Kea 1.3 tickets will modify the behavior of the CommandMgr
......@@ -1402,4 +1471,5 @@ TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeout) {
} // End of anonymous namespace
......@@ -20,6 +20,7 @@
#include <stats/stats_mgr.h>
#include <cfgrpt/config_report.h>
#include <signal.h>
#include <sstream>
using namespace isc::config;
using namespace isc::dhcp;
......@@ -377,6 +378,64 @@ ControlledDhcpv6Srv::commandConfigTestHandler(const string&,
return (checkConfig(dhcp6));
}
ConstElementPtr
ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
ConstElementPtr args) {
std::ostringstream message;
int64_t max_period = 0;
// Parse arguments to see if the 'max-period' parameter has been specified.
if (args) {
// Arguments must be a map.
if (args->getType() != Element::map) {
message << "arguments for the 'dhcp-disable' command must be a map";
} else {
ConstElementPtr max_period_element = args->get("max-period");
// max-period is optional.
if (max_period_element) {
// It must be an integer, if specified.
if (max_period_element->getType() != Element::integer) {
message << "'max-period' argument must be a number";
} else {
// It must be positive integer.
max_period = max_period_element->intValue();
if (max_period <= 0) {
message << "'max-period' must be positive integer";
}
// The user specified that the DHCP service should resume not
// later than in max-period seconds. If the 'dhcp-enable' command
// is not sent, the DHCP service will resume automatically.
network_state_.delayedEnableAll(static_cast<unsigned>(max_period));
}
}
}
}
// No error occurred, so let's disable the service.
if (message.tellp() == 0) {
network_state_.disableService();
message << "DHCPv6 service disabled";
if (max_period > 0) {
message << " for " << max_period << " seconds";
}
// Success.
return (config::createAnswer(CONTROL_RESULT_SUCCESS, message.str()));
}
// Failure.
return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
}
ConstElementPtr
ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&, ConstElementPtr) {
network_state_.enableService();
return (config::createAnswer(CONTROL_RESULT_SUCCESS, "DHCP service successfully enabled"));
}
ConstElementPtr
ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
ElementPtr extended = Element::create(Dhcpv6Srv::getVersion(true));
......@@ -457,6 +516,12 @@ ControlledDhcpv6Srv::processCommand(const std::string& command,
} else if (command == "config-test") {
return (srv->commandConfigTestHandler(command, args));
} else if (command == "dhcp-disable") {
return (srv->commandDhcpDisableHandler(command, args));
} else if (command == "dhcp-enable") {
return (srv->commandDhcpEnableHandler(command, args));
} else if (command == "version-get") {
return (srv->commandVersionGetHandler(command, args));
......@@ -638,6 +703,12 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port)
CommandMgr::instance().registerCommand("config-write",
boost::bind(&ControlledDhcpv6Srv::commandConfigWriteHandler, this, _1, _2));
CommandMgr::instance().registerCommand("dhcp-disable",
boost::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, _1, _2));
CommandMgr::instance().registerCommand("dhcp-enable",
boost::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, this, _1, _2));
CommandMgr::instance().registerCommand("leases-reclaim",
boost::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, _1, _2));
......@@ -694,6 +765,8 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
CommandMgr::instance().deregisterCommand("config-reload");
CommandMgr::instance().deregisterCommand("config-test");
CommandMgr::instance().deregisterCommand("config-write");
CommandMgr::instance().deregisterCommand("dhcp-disable");
CommandMgr::instance().deregisterCommand("dhcp-enable");
CommandMgr::instance().deregisterCommand("leases-reclaim");
CommandMgr::instance().deregisterCommand("libreload");
CommandMgr::instance().deregisterCommand("shutdown");
......
......@@ -223,6 +223,27 @@ private:
commandConfigTestHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief A handler for processing 'dhcp-disable' command.
///
/// @param command command name (ignored).
/// @param args aguments for the command. It must be a map and
/// it may include optional 'max-period' parameter.
///
/// @return result of the command.
isc::data::ConstElementPtr
commandDhcpDisableHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief A handler for processing 'dhcp-enable' command.
///
/// @param command command name (ignored)
/// @param args arguments for the command (ignored).
///
/// @return result of the command.
isc::data::ConstElementPtr
commandDhcpEnableHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @Brief handler for processing 'version-get' command
///
/// This handler processes version-get command, which returns
......
......@@ -409,8 +409,14 @@ server is about to open sockets on the specified port.
A warning message issued when IfaceMgr fails to open and bind a socket. The reason
for the failure is appended as an argument of the log message.
% DHCP6_PACKET_DROP_DHCP_DISABLED %1: DHCP service is globally disabled
This debug message is issued when a packet is dropped because the DHCP service
has been temporarily disabled. This affects all received DHCP packets. The
service may be enabled by the "dhcp-enable" control command or automatically
after a specified amount of time since receiving "dhcp-disable" command.
% DHCP6_PACKET_DROP_PARSE_FAIL failed to parse packet from %1 to %2, received over interface %3, reason: %4
The DHCPv4 server has received a packet that it is unable to
The DHCPv6 server has received a packet that it is unable to
interpret. The reason why the packet is invalid is included in the message.
% DHCP6_PACKET_DROP_SERVERID_MISMATCH %1: dropping packet with server identifier: %2, server is using: %3
......
......@@ -179,7 +179,7 @@ const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
: io_service_(new IOService()), port_(port), serverid_(), shutdown_(true),
alloc_engine_()
alloc_engine_(), name_change_reqs_(), network_state_(NetworkState::DHCPv6)
{
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
......@@ -468,7 +468,14 @@ void Dhcpv6Srv::run_one() {
return;
}
processPacket(query, rsp);
// If the DHCP service has been globally disabled, drop the packet.
if (!network_state_.isServiceEnabled()) {
LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_DETAIL_DATA,
DHCP6_PACKET_DROP_DHCP_DISABLED)
.arg(query->getLabel());
} else {
processPacket(query, rsp);
}
if (!rsp) {
return;
......
......@@ -19,6 +19,7 @@
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/network_state.h>
#include <dhcpsrv/subnet.h>
#include <hooks/callout_handle.h>
#include <dhcpsrv/daemon.h>
......@@ -871,6 +872,11 @@ protected:
/// Holds a list of @c isc::dhcp_ddns::NameChangeRequest objects, which
/// are waiting for sending to kea-dhcp-ddns module.
std::queue<isc::dhcp_ddns::NameChangeRequest> name_change_reqs_;
/// @brief Holds information about disabled DHCP service and/or
/// disabled subnet/network scopes.
NetworkState network_state_;
};
}; // namespace isc::dhcp
......
......@@ -83,6 +83,7 @@ public:
/// Expose internal methods for the sake of testing
using Dhcpv6Srv::receivePacket;
using Dhcpv6Srv::network_state_;
};
/// @brief Default control connection timeout.
......@@ -1175,6 +1176,74 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configReloadValid) {
::remove("test8.json");
}
// This test verifies if it is possible to disable DHCP service via command.
TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisable) {
createUnixChannelServer();
std::string response;
sendUnixCommand("{ \"command\": \"dhcp-disable\" }", response);
ConstElementPtr rsp;
// The response should be a valid JSON.
EXPECT_NO_THROW(rsp = Element::fromJSON(response));
ASSERT_TRUE(rsp);
int status;
ConstElementPtr cfg = parseAnswer(status, rsp);
EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
EXPECT_FALSE(server_->network_state_.isServiceEnabled());
}
// This test verifies that it is possible to disable DHCP service for a short
// period of time, after which the service is automatically enabled.
TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisableTemporarily) {
createUnixChannelServer();
std::string response;
// Send a command to disable DHCP service for 3 seconds.
sendUnixCommand("{"
" \"command\": \"dhcp-disable\","
" \"arguments\": {"
" \"max-period\": 3"
" }"
"}", response);