From: Marcin Siodelski Date: Thu, 11 Jul 2013 11:31:01 +0000 (+0200) Subject: [1555] Reopen active sockets when configuration changes. X-Git-Tag: bind10-1.2.0beta1-release~335^2~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0a5d26dfc2ee6a4a84d596cbcb39e894e7432f3c;p=thirdparty%2Fkea.git [1555] Reopen active sockets when configuration changes. --- diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index eb7d5599a0..9050a1651c 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -20,17 +20,18 @@ #include #include #include +#include #include #include #include #include #include +#include #include -#include -#include #include #include +#include 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); +} + }; }; diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 8f14f4b909..e5104b5d41 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -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_; diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 8aa913cffc..f414e5ae93 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -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); diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index f98233c4eb..59bb95c215 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -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 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 diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 1e97205287..dfe0f36604 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -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; } diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 35b4dc0248..442107db65 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -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.