|
@@ -1,4 +1,4 @@
|
|
-// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
|
|
|
|
|
|
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
|
//
|
|
//
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
@@ -13,36 +13,14 @@
|
|
// PERFORMANCE OF THIS SOFTWARE.
|
|
// PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
#include <config.h>
|
|
#include <config.h>
|
|
-
|
|
|
|
-#include <asiolink/asiolink.h>
|
|
|
|
#include <cc/data.h>
|
|
#include <cc/data.h>
|
|
-#include <cc/session.h>
|
|
|
|
-#include <config/ccsession.h>
|
|
|
|
-#include <dhcp/iface_mgr.h>
|
|
|
|
-#include <dhcp4/config_parser.h>
|
|
|
|
#include <dhcp4/ctrl_dhcp4_srv.h>
|
|
#include <dhcp4/ctrl_dhcp4_srv.h>
|
|
#include <dhcp4/dhcp4_log.h>
|
|
#include <dhcp4/dhcp4_log.h>
|
|
-#include <dhcp4/spec_config.h>
|
|
|
|
-#include <dhcpsrv/cfgmgr.h>
|
|
|
|
-#include <dhcpsrv/dhcp_config_parser.h>
|
|
|
|
-#include <exceptions/exceptions.h>
|
|
|
|
#include <hooks/hooks_manager.h>
|
|
#include <hooks/hooks_manager.h>
|
|
-#include <util/buffer.h>
|
|
|
|
-
|
|
|
|
-#include <cassert>
|
|
|
|
-#include <iostream>
|
|
|
|
-#include <sstream>
|
|
|
|
-#include <string>
|
|
|
|
-#include <vector>
|
|
|
|
|
|
+#include <dhcp4/json_config_parser.h>
|
|
|
|
|
|
-using namespace isc::asiolink;
|
|
|
|
-using namespace isc::cc;
|
|
|
|
-using namespace isc::config;
|
|
|
|
using namespace isc::data;
|
|
using namespace isc::data;
|
|
-using namespace isc::dhcp;
|
|
|
|
using namespace isc::hooks;
|
|
using namespace isc::hooks;
|
|
-using namespace isc::log;
|
|
|
|
-using namespace isc::util;
|
|
|
|
using namespace std;
|
|
using namespace std;
|
|
|
|
|
|
namespace isc {
|
|
namespace isc {
|
|
@@ -51,217 +29,141 @@ namespace dhcp {
|
|
ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL;
|
|
ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL;
|
|
|
|
|
|
ConstElementPtr
|
|
ConstElementPtr
|
|
-ControlledDhcpv4Srv::dhcp4StubConfigHandler(ConstElementPtr) {
|
|
|
|
- // This configuration handler is intended to be used only
|
|
|
|
- // when the initial configuration comes in. To receive this
|
|
|
|
- // configuration a pointer to this handler must be passed
|
|
|
|
- // using ModuleCCSession constructor. This constructor will
|
|
|
|
- // invoke the handler and will store the configuration for
|
|
|
|
- // the configuration session when the handler returns success.
|
|
|
|
- // Since this configuration is partial we just pretend to
|
|
|
|
- // parse it and always return success. The function that
|
|
|
|
- // initiates the session must get the configuration on its
|
|
|
|
- // own using getFullConfig.
|
|
|
|
- return (isc::config::createAnswer(0, "Configuration accepted."));
|
|
|
|
|
|
+ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) {
|
|
|
|
+ if (ControlledDhcpv4Srv::getInstance()) {
|
|
|
|
+ ControlledDhcpv4Srv::getInstance()->shutdown();
|
|
|
|
+ } else {
|
|
|
|
+ LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING);
|
|
|
|
+ ConstElementPtr answer = isc::config::createAnswer(1,
|
|
|
|
+ "Shutdown failure.");
|
|
|
|
+ return (answer);
|
|
|
|
+ }
|
|
|
|
+ ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down.");
|
|
|
|
+ return (answer);
|
|
}
|
|
}
|
|
|
|
|
|
ConstElementPtr
|
|
ConstElementPtr
|
|
-ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
|
|
|
|
- if (!server_ || !server_->config_session_) {
|
|
|
|
- // That should never happen as we install config_handler
|
|
|
|
- // after we instantiate the server.
|
|
|
|
- ConstElementPtr answer =
|
|
|
|
- isc::config::createAnswer(1, "Configuration rejected,"
|
|
|
|
- " server is during startup/shutdown phase.");
|
|
|
|
|
|
+ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
|
|
|
|
+
|
|
|
|
+ /// @todo delete any stored CalloutHandles referring to the old libraries
|
|
|
|
+ /// Get list of currently loaded libraries and reload them.
|
|
|
|
+ vector<string> loaded = HooksManager::getLibraryNames();
|
|
|
|
+ bool status = HooksManager::loadLibraries(loaded);
|
|
|
|
+ if (!status) {
|
|
|
|
+ LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);
|
|
|
|
+ ConstElementPtr answer = isc::config::createAnswer(1,
|
|
|
|
+ "Failed to reload hooks libraries.");
|
|
return (answer);
|
|
return (answer);
|
|
}
|
|
}
|
|
|
|
+ ConstElementPtr answer = isc::config::createAnswer(0,
|
|
|
|
+ "Hooks libraries successfully reloaded.");
|
|
|
|
+ return (answer);
|
|
|
|
+}
|
|
|
|
|
|
- // The configuration passed to this handler function is partial.
|
|
|
|
- // In other words, it just includes the values being modified.
|
|
|
|
- // In the same time, there are dependencies between various
|
|
|
|
- // DHCP configuration parsers. For example: the option value can
|
|
|
|
- // be set if the definition of this option is set. If someone removes
|
|
|
|
- // an existing option definition then the partial configuration that
|
|
|
|
- // removes that definition is triggered while a relevant option value
|
|
|
|
- // may remain configured. This eventually results in the DHCP server
|
|
|
|
- // configuration being in the inconsistent state.
|
|
|
|
- // In order to work around this problem we need to merge the new
|
|
|
|
- // configuration with the existing (full) configuration.
|
|
|
|
-
|
|
|
|
- // Let's create a new object that will hold the merged configuration.
|
|
|
|
- boost::shared_ptr<MapElement> merged_config(new MapElement());
|
|
|
|
- // Let's get the existing configuration.
|
|
|
|
- ConstElementPtr full_config = server_->config_session_->getFullConfig();
|
|
|
|
- // The full_config and merged_config should be always non-NULL
|
|
|
|
- // but to provide some level of exception safety we check that they
|
|
|
|
- // really are (in case we go out of memory).
|
|
|
|
- if (full_config && merged_config) {
|
|
|
|
- merged_config->setValue(full_config->mapValue());
|
|
|
|
-
|
|
|
|
- // Merge an existing and new configuration.
|
|
|
|
- isc::data::merge(merged_config, new_config);
|
|
|
|
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
|
|
|
|
- .arg(full_config->str());
|
|
|
|
- }
|
|
|
|
|
|
+ConstElementPtr
|
|
|
|
+ControlledDhcpv4Srv::commandConfigReloadHandler(const string&,
|
|
|
|
+ ConstElementPtr args) {
|
|
|
|
+ return (processConfig(args));
|
|
|
|
+}
|
|
|
|
|
|
- // Configure the server.
|
|
|
|
- ConstElementPtr answer = configureDhcp4Server(*server_, merged_config);
|
|
|
|
|
|
+ConstElementPtr
|
|
|
|
+ControlledDhcpv4Srv::processCommand(const string& command,
|
|
|
|
+ ConstElementPtr args) {
|
|
|
|
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED)
|
|
|
|
+ .arg(command).arg(args->str());
|
|
|
|
|
|
- // Check that configuration was successful. If not, do not reopen sockets.
|
|
|
|
- int rcode = 0;
|
|
|
|
- parseAnswer(rcode, answer);
|
|
|
|
- if (rcode != 0) {
|
|
|
|
- return (answer);
|
|
|
|
- }
|
|
|
|
|
|
+ ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
|
|
|
|
|
|
- // Server will start DDNS communications if its enabled.
|
|
|
|
- try {
|
|
|
|
- server_->startD2();
|
|
|
|
- } catch (const std::exception& ex) {
|
|
|
|
- std::ostringstream err;
|
|
|
|
- err << "error starting DHCP_DDNS client "
|
|
|
|
- " after server reconfiguration: " << ex.what();
|
|
|
|
- return (isc::config::createAnswer(1, err.str()));
|
|
|
|
|
|
+ if (!srv) {
|
|
|
|
+ ConstElementPtr no_srv = isc::config::createAnswer(1,
|
|
|
|
+ "Server object not initialized, so can't process command '" +
|
|
|
|
+ command + "', arguments: '" + args->str() + "'.");
|
|
|
|
+ return (no_srv);
|
|
}
|
|
}
|
|
|
|
|
|
- // Configuration may change active interfaces. Therefore, we have to reopen
|
|
|
|
- // sockets according to new configuration. This operation is not exception
|
|
|
|
- // safe and we really don't want to emit exceptions to the callback caller.
|
|
|
|
- // Instead, catch an exception and create appropriate answer.
|
|
|
|
try {
|
|
try {
|
|
- server_->openActiveSockets(server_->getPort(), server_->useBroadcast());
|
|
|
|
- } catch (std::exception& ex) {
|
|
|
|
- std::ostringstream err;
|
|
|
|
- err << "failed to open sockets after server reconfiguration: " << ex.what();
|
|
|
|
- answer = isc::config::createAnswer(1, err.str());
|
|
|
|
- }
|
|
|
|
- return (answer);
|
|
|
|
-}
|
|
|
|
|
|
+ if (command == "shutdown") {
|
|
|
|
+ return (srv->commandShutdownHandler(command, args));
|
|
|
|
|
|
-ConstElementPtr
|
|
|
|
-ControlledDhcpv4Srv::dhcp4CommandHandler(const string& command, ConstElementPtr args) {
|
|
|
|
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED)
|
|
|
|
- .arg(command).arg(args->str());
|
|
|
|
|
|
+ } else if (command == "libreload") {
|
|
|
|
+ return (srv->commandLibReloadHandler(command, args));
|
|
|
|
|
|
- if (command == "shutdown") {
|
|
|
|
- if (ControlledDhcpv4Srv::server_) {
|
|
|
|
- ControlledDhcpv4Srv::server_->shutdown();
|
|
|
|
- } else {
|
|
|
|
- LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING);
|
|
|
|
- ConstElementPtr answer = isc::config::createAnswer(1,
|
|
|
|
- "Shutdown failure.");
|
|
|
|
- return (answer);
|
|
|
|
- }
|
|
|
|
- ConstElementPtr answer = isc::config::createAnswer(0,
|
|
|
|
- "Shutting down.");
|
|
|
|
- return (answer);
|
|
|
|
|
|
+ } else if (command == "config-reload") {
|
|
|
|
+ return (srv->commandConfigReloadHandler(command, args));
|
|
|
|
|
|
- } else if (command == "libreload") {
|
|
|
|
- // TODO delete any stored CalloutHandles referring to the old libraries
|
|
|
|
- // Get list of currently loaded libraries and reload them.
|
|
|
|
- vector<string> loaded = HooksManager::getLibraryNames();
|
|
|
|
- bool status = HooksManager::loadLibraries(loaded);
|
|
|
|
- if (!status) {
|
|
|
|
- LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);
|
|
|
|
- ConstElementPtr answer = isc::config::createAnswer(1,
|
|
|
|
- "Failed to reload hooks libraries.");
|
|
|
|
- return (answer);
|
|
|
|
}
|
|
}
|
|
- ConstElementPtr answer = isc::config::createAnswer(0,
|
|
|
|
- "Hooks libraries successfully reloaded.");
|
|
|
|
|
|
+ ConstElementPtr answer = isc::config::createAnswer(1,
|
|
|
|
+ "Unrecognized command:" + command);
|
|
return (answer);
|
|
return (answer);
|
|
|
|
+ } catch (const Exception& ex) {
|
|
|
|
+ return (isc::config::createAnswer(1, "Error while processing command '"
|
|
|
|
+ + command + "':" + ex.what() +
|
|
|
|
+ ", params: '" + args->str() + "'"));
|
|
}
|
|
}
|
|
-
|
|
|
|
- ConstElementPtr answer = isc::config::createAnswer(1,
|
|
|
|
- "Unrecognized command.");
|
|
|
|
-
|
|
|
|
- return (answer);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-void ControlledDhcpv4Srv::sessionReader(void) {
|
|
|
|
- // Process one asio event. If there are more events, iface_mgr will call
|
|
|
|
- // this callback more than once.
|
|
|
|
- if (server_) {
|
|
|
|
- server_->io_service_.run_one();
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+isc::data::ConstElementPtr
|
|
|
|
+ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
|
|
|
|
|
|
-void ControlledDhcpv4Srv::establishSession() {
|
|
|
|
|
|
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED)
|
|
|
|
+ .arg(config->str());
|
|
|
|
|
|
- string specfile;
|
|
|
|
- if (getenv("B10_FROM_BUILD")) {
|
|
|
|
- specfile = string(getenv("B10_FROM_BUILD")) +
|
|
|
|
- "/src/bin/dhcp4/dhcp4.spec";
|
|
|
|
- } else {
|
|
|
|
- specfile = string(DHCP4_SPECFILE_LOCATION);
|
|
|
|
- }
|
|
|
|
|
|
+ ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
|
|
|
|
|
|
- /// @todo: Check if session is not established already. Throw, if it is.
|
|
|
|
|
|
+ // Single stream instance used in all error clauses
|
|
|
|
+ std::ostringstream err;
|
|
|
|
|
|
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTING)
|
|
|
|
- .arg(specfile);
|
|
|
|
- cc_session_ = new Session(io_service_.get_io_service());
|
|
|
|
- // Create a session with the dummy configuration handler.
|
|
|
|
- // Dumy configuration handler is internally invoked by the
|
|
|
|
- // constructor and on success the constructor updates
|
|
|
|
- // the current session with the configuration that had been
|
|
|
|
- // committed in the previous session. If we did not install
|
|
|
|
- // the dummy handler, the previous configuration would have
|
|
|
|
- // been lost.
|
|
|
|
- config_session_ = new ModuleCCSession(specfile, *cc_session_,
|
|
|
|
- dhcp4StubConfigHandler,
|
|
|
|
- dhcp4CommandHandler, false);
|
|
|
|
- config_session_->start();
|
|
|
|
|
|
+ if (!srv) {
|
|
|
|
+ err << "Server object not initialized, can't process config.";
|
|
|
|
+ return (isc::config::createAnswer(1, err.str()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ConstElementPtr answer = configureDhcp4Server(*srv, config);
|
|
|
|
|
|
- // We initially create ModuleCCSession() without configHandler, as
|
|
|
|
- // the session module is too eager to send partial configuration.
|
|
|
|
- // We want to get the full configuration, so we explicitly call
|
|
|
|
- // getFullConfig() and then pass it to our configHandler.
|
|
|
|
- config_session_->setConfigHandler(dhcp4ConfigHandler);
|
|
|
|
|
|
|
|
|
|
+ // Check that configuration was successful. If not, do not reopen sockets
|
|
|
|
+ // and don't bother with DDNS stuff.
|
|
try {
|
|
try {
|
|
- configureDhcp4Server(*this, config_session_->getFullConfig());
|
|
|
|
-
|
|
|
|
- // Server will start DDNS communications if its enabled.
|
|
|
|
- server_->startD2();
|
|
|
|
-
|
|
|
|
- // Configuration may disable or enable interfaces so we have to
|
|
|
|
- // reopen sockets according to new configuration.
|
|
|
|
- openActiveSockets(getPort(), useBroadcast());
|
|
|
|
-
|
|
|
|
|
|
+ int rcode = 0;
|
|
|
|
+ isc::config::parseAnswer(rcode, answer);
|
|
|
|
+ if (rcode != 0) {
|
|
|
|
+ return (answer);
|
|
|
|
+ }
|
|
} catch (const std::exception& ex) {
|
|
} catch (const std::exception& ex) {
|
|
- LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
|
|
|
|
-
|
|
|
|
|
|
+ err << "Failed to process configuration:" << ex.what();
|
|
|
|
+ return (isc::config::createAnswer(1, err.str()));
|
|
}
|
|
}
|
|
|
|
|
|
- /// Integrate the asynchronous I/O model of BIND 10 configuration
|
|
|
|
- /// control with the "select" model of the DHCP server. This is
|
|
|
|
- /// fully explained in \ref dhcpv4Session.
|
|
|
|
- int ctrl_socket = cc_session_->getSocketDesc();
|
|
|
|
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
|
|
|
|
- .arg(ctrl_socket);
|
|
|
|
- IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void ControlledDhcpv4Srv::disconnectSession() {
|
|
|
|
- if (config_session_) {
|
|
|
|
- delete config_session_;
|
|
|
|
- config_session_ = NULL;
|
|
|
|
|
|
+ // Server will start DDNS communications if its enabled.
|
|
|
|
+ try {
|
|
|
|
+ srv->startD2();
|
|
|
|
+ } catch (const std::exception& ex) {
|
|
|
|
+ err << "Error starting DHCP_DDNS client after server reconfiguration: "
|
|
|
|
+ << ex.what();
|
|
|
|
+ return (isc::config::createAnswer(1, err.str()));
|
|
}
|
|
}
|
|
- if (cc_session_) {
|
|
|
|
|
|
|
|
- int ctrl_socket = cc_session_->getSocketDesc();
|
|
|
|
- cc_session_->disconnect();
|
|
|
|
-
|
|
|
|
- IfaceMgr::instance().deleteExternalSocket(ctrl_socket);
|
|
|
|
- delete cc_session_;
|
|
|
|
- cc_session_ = NULL;
|
|
|
|
|
|
+ // Configuration may change active interfaces. Therefore, we have to reopen
|
|
|
|
+ // sockets according to new configuration. This operation is not exception
|
|
|
|
+ // safe and we really don't want to emit exceptions to whoever called this
|
|
|
|
+ // method. Instead, catch an exception and create appropriate answer.
|
|
|
|
+ try {
|
|
|
|
+ srv->openActiveSockets(srv->getPort(), getInstance()->useBroadcast());
|
|
|
|
+ } catch (std::exception& ex) {
|
|
|
|
+ err << "failed to open sockets after server reconfiguration: "
|
|
|
|
+ << ex.what();
|
|
|
|
+ answer = isc::config::createAnswer(1, err.str());
|
|
}
|
|
}
|
|
|
|
+ return (answer);
|
|
}
|
|
}
|
|
|
|
|
|
ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
|
|
ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
|
|
- :Dhcpv4Srv(port), cc_session_(NULL), config_session_(NULL) {
|
|
|
|
- server_ = this; // remember this instance for use in callback
|
|
|
|
|
|
+ :Dhcpv4Srv(port) {
|
|
|
|
+ if (getInstance()) {
|
|
|
|
+ isc_throw(InvalidOperation,
|
|
|
|
+ "There is another Dhcpv4Srv instance already.");
|
|
|
|
+ }
|
|
|
|
+ server_ = this; // remember this instance for later use in handlers
|
|
}
|
|
}
|
|
|
|
|
|
void ControlledDhcpv4Srv::shutdown() {
|
|
void ControlledDhcpv4Srv::shutdown() {
|
|
@@ -270,22 +172,19 @@ void ControlledDhcpv4Srv::shutdown() {
|
|
}
|
|
}
|
|
|
|
|
|
ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
|
|
ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
|
|
- disconnectSession();
|
|
|
|
-
|
|
|
|
- server_ = NULL; // forget this instance. There should be no callback anymore
|
|
|
|
- // at this stage anyway.
|
|
|
|
|
|
+ cleanup();
|
|
|
|
+
|
|
|
|
+ server_ = NULL; // forget this instance. Noone should call any handlers at
|
|
|
|
+ // this stage.
|
|
}
|
|
}
|
|
|
|
|
|
-isc::data::ConstElementPtr
|
|
|
|
-ControlledDhcpv4Srv::execDhcpv4ServerCommand(const std::string& command_id,
|
|
|
|
- isc::data::ConstElementPtr args) {
|
|
|
|
- try {
|
|
|
|
- return (dhcp4CommandHandler(command_id, args));
|
|
|
|
- } catch (const Exception& ex) {
|
|
|
|
- ConstElementPtr answer = isc::config::createAnswer(1, ex.what());
|
|
|
|
- return (answer);
|
|
|
|
|
|
+void ControlledDhcpv4Srv::sessionReader(void) {
|
|
|
|
+ // Process one asio event. If there are more events, iface_mgr will call
|
|
|
|
+ // this callback more than once.
|
|
|
|
+ if (getInstance()) {
|
|
|
|
+ getInstance()->io_service_.run_one();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-};
|
|
|
|
-};
|
|
|
|
|
|
+}; // end of isc::dhcp namespace
|
|
|
|
+}; // end of isc namespace
|