Commit 0a5d26df authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[1555] Reopen active sockets when configuration changes.

parent 9517e24b
......@@ -20,17 +20,18 @@
#include <config/ccsession.h>
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/spec_config.h>
#include <dhcp4/config_parser.h>
#include <exceptions/exceptions.h>
#include <boost/bind.hpp>
#include <util/buffer.h>
#include <cassert>
#include <iostream>
#include <cassert>
#include <iostream>
#include <sstream>
using namespace isc::asiolink;
using namespace isc::cc;
......@@ -101,7 +102,27 @@ ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
}
// Configure the server.
return (configureDhcp4Server(*server_, merged_config));
ConstElementPtr answer = configureDhcp4Server(*server_, merged_config);
// Check that configuration was successful. If not, do not reopen sockets.
int rcode = 0;
parseAnswer(rcode, answer);
if (rcode != 0) {
return (answer);
}
// 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 {
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);
}
ConstElementPtr
......@@ -228,6 +249,25 @@ ControlledDhcpv4Srv::execDhcpv4ServerCommand(const std::string& command_id,
}
}
void
ControlledDhcpv4Srv::openActiveSockets(const uint16_t port, const bool use_bcast) {
IfaceMgr::instance().closeSockets();
// Get the reference to the collection of interfaces. This reference should be
// valid as long as the program is run because IfaceMgr is a singleton.
// Therefore we can safely iterate over instances of all interfaces and modify
// their flags. Here we modify flags which indicate wheter socket should be
// open for a particular interface or not.
IfaceMgr::IfaceCollection ifaces = IfaceMgr::instance().getIfaces();
for (IfaceMgr::IfaceCollection::iterator iface = ifaces.begin();
iface != ifaces.end(); ++iface) {
iface->inactive_ = !CfgMgr::instance().isActiveIface(iface->getName());
}
// Let's reopen active sockets. openSockets4 will check internally whether
// sockets are marked active or inactive.
IfaceMgr::instance().openSockets4(port, use_bcast);
}
};
};
......@@ -130,6 +130,17 @@ protected:
/// when there is a new command or configuration sent over msgq.
static void sessionReader(void);
/// @brief Open sockets which are marked as active in @c CfgMgr.
///
/// This function reopens sockets according to the current settings in the
/// Configuration Manager. It holds the list of the interfaces which server
/// should listen on. This function will open sockets on these interfaces
/// only. This function is not exception safe.
///
/// @param port UDP port on which server should listen.
/// @param use_bcast should broadcast flags be set on the sockets.
static void openActiveSockets(const uint16_t port, const bool use_bcast);
/// @brief IOService object, used for all ASIO operations.
isc::asiolink::IOService io_service_;
......
......@@ -58,7 +58,8 @@ static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
// grants those options and a single, fixed, hardcoded lease.
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
const bool direct_response_desired) {
const bool direct_response_desired)
: port_(port), use_bcast_(use_bcast) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
try {
// First call to instance() will create IfaceMgr (it's a singleton)
......@@ -73,7 +74,7 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
if (port) {
// open sockets only if port is non-zero. Port 0 is used
// for non-socket related testing.
IfaceMgr::instance().openSockets4(port, use_bcast);
IfaceMgr::instance().openSockets4(port_, use_bcast_);
}
string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
......
......@@ -113,6 +113,32 @@ public:
/// be freed by the caller.
static const char* serverReceivedPacketName(uint8_t type);
///
/// @name Public accessors returning values required to (re)open sockets.
///
/// These accessors must be public because sockets are reopened from the
/// static configuration callback handler. This callback handler invokes
/// @c ControlledDhcpv4Srv::openActiveSockets which requires parameters
/// which has to be retrieved from the @c ControlledDhcpv4Srv object.
/// They are retrieved using these public functions
//@{
///
/// @brief Get UDP port on which server should listen.
///
/// @return UDP port on which server should listen.
uint16_t getPort() const {
return (port_);
}
/// @brief Return bool value indicating that broadcast flags should be set
/// on sockets.
///
/// @return A bool value indicating that broadcast should be used (if true).
bool useBroadcast() const {
return (use_bcast_);
}
//@}
protected:
/// @brief verifies if specified packet meets RFC requirements
......@@ -310,6 +336,9 @@ private:
/// during normal operation (e.g. to use different allocators)
boost::shared_ptr<AllocEngine> alloc_engine_;
uint16_t port_; ///< UDP port number on which server listens.
bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
};
}; // namespace isc::dhcp
......
......@@ -51,7 +51,8 @@ IfaceMgr::instance() {
Iface::Iface(const std::string& name, int ifindex)
:name_(name), ifindex_(ifindex), mac_len_(0), hardware_type_(0),
flag_loopback_(false), flag_up_(false), flag_running_(false),
flag_multicast_(false), flag_broadcast_(false), flags_(0)
flag_multicast_(false), flag_broadcast_(false), flags_(0),
inactive_(false)
{
memset(mac_, 0, sizeof(mac_));
}
......@@ -295,7 +296,8 @@ bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
if (iface->flag_loopback_ ||
!iface->flag_up_ ||
!iface->flag_running_) {
!iface->flag_running_,
iface->inactive_) {
continue;
}
......@@ -361,7 +363,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
if (iface->flag_loopback_ ||
!iface->flag_up_ ||
!iface->flag_running_) {
!iface->flag_running_,
!iface->inactive_) {
continue;
}
......
......@@ -309,6 +309,10 @@ public:
/// Interface flags (this value is as is returned by OS,
/// it may mean different things on different OSes).
uint32_t flags_;
/// Interface is inactive. This can be explicitly set to prevent Interface
/// Manager from opening the socket on this interface.
bool inactive_;
};
/// @brief Handles network interfaces, transmission and reception.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment