From: Razvan Becheriu Date: Wed, 4 Sep 2019 15:09:27 +0000 (+0300) Subject: updated multi-thread packet processing X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f877328c4263f81ef7f4735f398c864ca402d7af;p=thirdparty%2Fkea.git updated multi-thread packet processing --- diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 5618e231ff..bf67cedfed 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -5,30 +5,38 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include -#include + #include +#include +#include #include +#include #include -#include -#include #include #include #include #include #include +#include +#include +#include #include #include #include -#include +#include + #include + +#include #include using namespace isc::config; -using namespace isc::db; using namespace isc::data; +using namespace isc::db; using namespace isc::dhcp; using namespace isc::hooks; using namespace isc::stats; +using namespace isc::util::thread; using namespace std; namespace { @@ -77,34 +85,6 @@ namespace dhcp { ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL; -void -ControlledDhcpv4Srv::init(const std::string& file_name) { - // Configure the server using JSON file. - ConstElementPtr result = loadConfigFile(file_name); - int rcode; - ConstElementPtr comment = isc::config::parseAnswer(rcode, result); - if (rcode != 0) { - string reason = comment ? comment->stringValue() : - "no details available"; - isc_throw(isc::BadValue, reason); - } - - // We don't need to call openActiveSockets() or startD2() as these - // methods are called in processConfig() which is called by - // processCommand("config-set", ...) - - // Set signal handlers. When the SIGHUP is received by the process - // the server reconfiguration will be triggered. When SIGTERM or - // SIGINT will be received, the server will start shutting down. - signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM)); - // Set the pointer to the handler function. - signal_handler_ = signalHandler; -} - -void ControlledDhcpv4Srv::cleanup() { - // Nothing to do here. No need to disconnect from anything. -} - /// @brief Configure DHCPv4 server using the configuration file specified. /// /// This function is used to both configure the DHCP server on its startup @@ -121,16 +101,14 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) { // configuration from a JSON file. isc::data::ConstElementPtr json; - isc::data::ConstElementPtr dhcp4; - isc::data::ConstElementPtr logger; isc::data::ConstElementPtr result; // Basic sanity check: file name must not be empty. try { if (file_name.empty()) { // Basic sanity check: file name must not be empty. - isc_throw(isc::BadValue, "JSON configuration file not specified." - " Please use -c command line option."); + isc_throw(isc::BadValue, "JSON configuration file not specified. Please " + "use -c command line option."); } // Read contents of the file and parse it as JSON @@ -184,6 +162,34 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) { return (result); } +void +ControlledDhcpv4Srv::init(const std::string& file_name) { + // Configure the server using JSON file. + ConstElementPtr result = loadConfigFile(file_name); + + int rcode; + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); + if (rcode != 0) { + string reason = comment ? comment->stringValue() : + "no details available"; + isc_throw(isc::BadValue, reason); + } + + // We don't need to call openActiveSockets() or startD2() as these + // methods are called in processConfig() which is called by + // processCommand("config-set", ...) + + // Set signal handlers. When the SIGHUP is received by the process + // the server reconfiguration will be triggered. When SIGTERM or + // SIGINT will be received, the server will start shutting down. + signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM)); + // Set the pointer to the handler function. + signal_handler_ = signalHandler; +} + +void ControlledDhcpv4Srv::cleanup() { + // Nothing to do here. No need to disconnect from anything. +} ConstElementPtr ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) { @@ -191,28 +197,26 @@ ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) { ControlledDhcpv4Srv::getInstance()->shutdown(); } else { LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING); - ConstElementPtr answer = isc::config::createAnswer(1, - "Shutdown failure."); + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure."); return (answer); } - ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down."); + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down."); return (answer); } ConstElementPtr ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) { - /// @todo delete any stored CalloutHandles referring to the old libraries /// Get list of currently loaded libraries and reload them. HookLibsCollection loaded = HooksManager::getLibraryInfo(); bool status = HooksManager::loadLibraries(loaded); if (!status) { LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL); - ConstElementPtr answer = isc::config::createAnswer(1, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Failed to reload hooks libraries."); return (answer); } - ConstElementPtr answer = isc::config::createAnswer(0, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Hooks libraries successfully reloaded."); return (answer); } @@ -220,7 +224,6 @@ ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) { ConstElementPtr ControlledDhcpv4Srv::commandConfigReloadHandler(const string&, ConstElementPtr /*args*/) { - // Get configuration file name. std::string file = ControlledDhcpv4Srv::getInstance()->getConfigFile(); try { @@ -246,8 +249,7 @@ ControlledDhcpv4Srv::commandConfigGetHandler(const string&, } ConstElementPtr -ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, - ConstElementPtr args) { +ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, ConstElementPtr args) { string filename; if (args) { @@ -266,6 +268,7 @@ ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, if (filename.empty()) { // filename parameter was not specified, so let's use whatever we remember + // from the command-line filename = getConfigFile(); } @@ -300,7 +303,7 @@ ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, ConstElementPtr ControlledDhcpv4Srv::commandConfigSetHandler(const string&, ConstElementPtr args) { - const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error + const int status_code = CONTROL_RESULT_ERROR; ConstElementPtr dhcp4; string message; @@ -371,7 +374,7 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&, // the logging first in case there's a configuration failure. int rcode = 0; isc::config::parseAnswer(rcode, result); - if (rcode == 0) { + if (rcode == CONTROL_RESULT_SUCCESS) { CfgMgr::instance().getStagingCfg()->applyLoggingCfg(); // Use new configuration. @@ -493,17 +496,16 @@ ControlledDhcpv4Srv::commandVersionGetHandler(const string&, ConstElementPtr) { ElementPtr extended = Element::create(Dhcpv4Srv::getVersion(true)); ElementPtr arguments = Element::createMap(); arguments->set("extended", extended); - ConstElementPtr answer = isc::config::createAnswer(0, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, Dhcpv4Srv::getVersion(false), arguments); return (answer); } ConstElementPtr -ControlledDhcpv4Srv::commandBuildReportHandler(const string&, - ConstElementPtr) { +ControlledDhcpv4Srv::commandBuildReportHandler(const string&, ConstElementPtr) { ConstElementPtr answer = - isc::config::createAnswer(0, isc::detail::getConfigReport()); + isc::config::createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport()); return (answer); } @@ -585,12 +587,17 @@ ControlledDhcpv4Srv::processCommand(const string& command, ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance(); if (!srv) { - ConstElementPtr no_srv = isc::config::createAnswer(1, - "Server object not initialized, so can't process command '" + + ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR, + "Server object not initialized, can't process command '" + command + "', arguments: '" + txt + "'."); return (no_srv); } + if (srv->run_multithreaded_) { + srv->pkt_thread_pool_.destroy(); + srv->pkt_thread_pool_.create(Dhcpv4Srv::threadCount()); + } + try { if (command == "shutdown") { return (srv->commandShutdownHandler(command, args)); @@ -635,12 +642,11 @@ ControlledDhcpv4Srv::processCommand(const string& command, return (srv->commandConfigBackendPullHandler(command, args)); } - ConstElementPtr answer = isc::config::createAnswer(1, - "Unrecognized command:" + command); - return (answer); + return(isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unrecognized command: " + + command)); } catch (const Exception& ex) { - return (isc::config::createAnswer(1, "Error while processing command '" - + command + "':" + ex.what() + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Error while processing command '" + + command + "': " + ex.what() + ", params: '" + txt + "'")); } } @@ -658,7 +664,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { if (!srv) { err << "Server object not initialized, can't process config."; - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } ConstElementPtr answer = configureDhcp4Server(*srv, config); @@ -672,8 +678,8 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - err << "Failed to process configuration:" << ex.what(); - return (isc::config::createAnswer(1, err.str())); + err << "Failed to process configuration: " << ex.what(); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Re-open lease and host database with new parameters. @@ -685,7 +691,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { cfg_db->createManagers(); } catch (const std::exception& ex) { err << "Unable to open database: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Server will start DDNS communications if its enabled. @@ -694,7 +700,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { } catch (const std::exception& ex) { err << "Error starting DHCP_DDNS client after server reconfiguration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Setup DHCPv4-over-DHCPv6 IPC @@ -704,7 +710,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { std::ostringstream err; err << "error starting DHCPv4-over-DHCPv6 IPC " " after server reconfiguration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Configure DHCP packet queueing @@ -713,7 +719,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl(); if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) { LOG_INFO(dhcp4_logger, DHCP4_CONFIG_PACKET_QUEUE) - .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr()); + .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr()); } } catch (const std::exception& ex) { @@ -744,9 +750,10 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { err << "unable to setup timers for periodically running the" " reclamation of the expired leases: " << ex.what() << "."; - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } + // Setup config backend polling, if configured for it. auto ctl_info = CfgMgr::instance().getStagingCfg()->getConfigControlInfo(); if (ctl_info) { long fetch_time = static_cast(ctl_info->getConfigFetchWaitTime()); @@ -805,7 +812,7 @@ isc::data::ConstElementPtr ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) { LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED) - .arg(config->str()); + .arg(config->str()); ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance(); @@ -814,15 +821,16 @@ ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) { if (!srv) { err << "Server object not initialized, can't process config."; - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } return (configureDhcp4Server(*srv, config, true)); } ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_PORT*/, - uint16_t client_port /*= 0*/) - : Dhcpv4Srv(server_port, client_port), io_service_(), + uint16_t client_port /*= 0*/, + bool run_multithreaded /*= false*/) + : Dhcpv4Srv(server_port, client_port, run_multithreaded), io_service_(), timer_mgr_(TimerMgr::instance()) { if (getInstance()) { isc_throw(InvalidOperation, @@ -884,18 +892,18 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_P CommandMgr::instance().registerCommand("statistic-get", boost::bind(&StatsMgr::statisticGetHandler, _1, _2)); - CommandMgr::instance().registerCommand("statistic-reset", - boost::bind(&StatsMgr::statisticResetHandler, _1, _2)); - - CommandMgr::instance().registerCommand("statistic-remove", - boost::bind(&StatsMgr::statisticRemoveHandler, _1, _2)); - CommandMgr::instance().registerCommand("statistic-get-all", boost::bind(&StatsMgr::statisticGetAllHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-reset", + boost::bind(&StatsMgr::statisticResetHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-reset-all", boost::bind(&StatsMgr::statisticResetAllHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-remove", + boost::bind(&StatsMgr::statisticRemoveHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-remove-all", boost::bind(&StatsMgr::statisticRemoveAllHandler, _1, _2)); @@ -934,6 +942,7 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { CommandMgr::instance().deregisterCommand("build-report"); CommandMgr::instance().deregisterCommand("config-backend-pull"); CommandMgr::instance().deregisterCommand("config-get"); + CommandMgr::instance().deregisterCommand("config-set"); CommandMgr::instance().deregisterCommand("config-reload"); CommandMgr::instance().deregisterCommand("config-set"); CommandMgr::instance().deregisterCommand("config-test"); @@ -962,8 +971,8 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { ; } - server_ = NULL; // forget this instance. Noone should call any handlers at - // this stage. + server_ = NULL; // forget this instance. There should be no callback anymore + // at this stage anyway. } void ControlledDhcpv4Srv::sessionReader(void) { @@ -1098,5 +1107,5 @@ ControlledDhcpv4Srv::cbFetchUpdates(const SrvConfigPtr& srv_cfg, } } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index d67501da50..b1b6bb32d5 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -29,11 +29,13 @@ public: /// /// @param server_port UDP port to be opened for DHCP traffic /// @param client_port UDP port where all responses are sent to. + /// @param run_multithreaded enables or disables multithreaded mode ControlledDhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT, - uint16_t client_port = 0); + uint16_t client_port = 0, + bool run_multithreaded = false); /// @brief Destructor. - ~ControlledDhcpv4Srv(); + virtual ~ControlledDhcpv4Srv(); /// @brief Initializes the server. /// @@ -43,12 +45,12 @@ public: /// This method may throw if initialization fails. void init(const std::string& config_file); - /// @brief Loads specific config file + /// @brief Loads specific configuration file /// /// This utility method is called whenever we know a filename of the config /// and need to load it. It calls config-set command once the content of /// the file has been loaded and verified to be a sane JSON configuration. - /// config-set handler will process the config file (load it as current + /// config-set handler will process the config file (apply it as current /// configuration). /// /// @param file_name name of the file to be loaded @@ -121,12 +123,11 @@ public: return (server_); } - private: /// @brief Callback that will be called from iface_mgr when data /// is received over control socket. /// - /// This static callback method is called from IfaceMgr::receive6() method, + /// This static callback method is called from IfaceMgr::receive4() method, /// when there is a new command or configuration sent over control socket /// (that was sent from some yet unspecified sender). static void sessionReader(void); @@ -249,7 +250,6 @@ private: commandDhcpEnableHandler(const std::string& command, isc::data::ConstElementPtr args); - /// @Brief handler for processing 'version-get' command /// /// This handler processes version-get command, which returns @@ -404,7 +404,7 @@ private: /// @brief Static pointer to the sole instance of the DHCP server. /// /// This is required for config and command handlers to gain access to - /// the server + /// the server. Some of them need to be static methods. static ControlledDhcpv4Srv* server_; /// @brief IOService object, used for all ASIO operations. @@ -417,7 +417,7 @@ private: TimerMgrPtr timer_mgr_; }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 1b4b155561..1aacafdaa0 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,7 @@ #include #include #include -#include +#include #include #include #include @@ -71,6 +70,7 @@ #include #include + using namespace isc; using namespace isc::asiolink; using namespace isc::cryptolink; @@ -81,6 +81,8 @@ using namespace isc::log; using namespace isc::stats; using namespace std; +using isc::util::thread::LockGuard; + namespace { /// Structure that holds registered hook indexes @@ -101,8 +103,8 @@ struct Dhcp4Hooks { hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive"); hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select"); hook_index_leases4_committed_ = HooksManager::registerHook("leases4_committed"); - hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send"); hook_index_lease4_release_ = HooksManager::registerHook("lease4_release"); + hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send"); hook_index_buffer4_send_ = HooksManager::registerHook("buffer4_send"); hook_index_lease4_decline_ = HooksManager::registerHook("lease4_decline"); hook_index_host4_identifier_ = HooksManager::registerHook("host4_identifier"); @@ -208,7 +210,7 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, .arg(query_->getLabel()) .arg(classes.toText()); } -}; +} void Dhcpv4Exchange::initResponse() { @@ -464,15 +466,18 @@ Dhcpv4Exchange::setReservedMessageFields() { const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_"); Dhcpv4Srv::Dhcpv4Srv(uint16_t server_port, uint16_t client_port, + bool run_multithreaded /* = false */, const bool use_bcast, const bool direct_response_desired) - : io_service_(new IOService()), shutdown_(true), alloc_engine_(), - use_bcast_(use_bcast), server_port_(server_port), - client_port_(client_port), + : io_service_(new IOService()), server_port_(server_port), + client_port_(client_port), use_bcast_(use_bcast), + shutdown_(true), alloc_engine_(), network_state_(new NetworkState(NetworkState::DHCPv4)), - cb_control_(new CBControlDHCPv4()) { + cb_control_(new CBControlDHCPv4()), + run_multithreaded_(run_multithreaded) { LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET) .arg(server_port); + try { // Port 0 is used for testing purposes where we don't open broadcast // capable sockets. So, set the packet filter handling direct traffic @@ -767,6 +772,14 @@ Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) { bool Dhcpv4Srv::run() { + if (run_multithreaded_) { + // Creating the process packet thread pool + // The number of thread pool's threads should be read from configuration + // file or it should be determined by the number of hardware threads and + // the number of Cassandra DB nodes. + pkt_thread_pool_.create(Dhcpv4Srv::threadCount()); + } + while (!shutdown_) { try { run_one(); @@ -784,6 +797,11 @@ Dhcpv4Srv::run() { } } + // destroying the thread pool + if (run_multithreaded_) { + pkt_thread_pool_.destroy(); + } + return (true); } @@ -794,6 +812,18 @@ Dhcpv4Srv::run_one() { Pkt4Ptr rsp; try { + + // Do not read more packets from socket if there are enough + // packets to be processed in the packet thread pool queue + const int max_queued_pkt_per_thread = Dhcpv4Srv::maxThreadQueueSize(); + const auto queue_full_wait = std::chrono::milliseconds(1); + size_t pkt_queue_size = pkt_thread_pool_.count(); + if (pkt_queue_size >= Dhcpv4Srv::threadCount() * + max_queued_pkt_per_thread) { + std::this_thread::sleep_for(queue_full_wait); + return; + } + // Set select() timeout to 1s. This value should not be modified // because it is important that the select() returns control // frequently so as the IOService can be polled for ready handlers. @@ -867,9 +897,31 @@ Dhcpv4Srv::run_one() { .arg(query->getLabel()); return; } else { - processPacket(query, rsp); + if (run_multithreaded_) { + ThreadPool::WorkItemCallBack call_back = + std::bind(&Dhcpv4Srv::processPacketAndSendResponseNoThrow, this, query, rsp); + pkt_thread_pool_.add(call_back); + } else { + processPacketAndSendResponse(query, rsp); } + } +} +void +Dhcpv4Srv::processPacketAndSendResponseNoThrow(Pkt4Ptr& query, Pkt4Ptr& rsp) { + try { + processPacketAndSendResponse(query, rsp); + } catch (const std::exception& e) { + LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_STD_EXCEPTION) + .arg(e.what()); + } catch (...) { + LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_EXCEPTION); + } +} + +void +Dhcpv4Srv::processPacketAndSendResponse(Pkt4Ptr& query, Pkt4Ptr& rsp) { + processPacket(query, rsp); if (!rsp) { return; } @@ -3755,6 +3807,23 @@ int Dhcpv4Srv::getHookIndexLease4Decline() { return (Hooks.hook_index_lease4_decline_); } +uint32_t Dhcpv4Srv::threadCount() { + uint32_t sys_threads = CfgMgr::instance().getCurrentCfg()->getServerThreadCount(); + if (sys_threads) { + return sys_threads; + } + sys_threads = std::thread::hardware_concurrency(); + return sys_threads * 1; +} + +uint32_t Dhcpv4Srv::maxThreadQueueSize() { + uint32_t max_thread_queue_size = CfgMgr::instance().getCurrentCfg()->getServerMaxThreadQueueSize(); + if (max_thread_queue_size) { + return max_thread_queue_size; + } + return 4; +} + void Dhcpv4Srv::discardPackets() { // Clear any packets held by the callhout handle store and // all parked packets @@ -3763,5 +3832,5 @@ void Dhcpv4Srv::discardPackets() { HooksManager::clearParkingLots(); } -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index 13265a5e29..8b9748f7b8 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -15,10 +15,11 @@ #include #include #include +#include #include +#include #include #include -#include #include #include #include @@ -30,6 +31,8 @@ #include #include #include +#include +#include // Undefine the macro OPTIONAL which is defined in some operating // systems but conflicts with a member of the RequirementLevel enum in @@ -199,7 +202,6 @@ private: asiolink::IOServicePtr io_service_; public: - /// @brief defines if certain option may, must or must not appear typedef enum { FORBIDDEN, @@ -223,20 +225,22 @@ public: /// /// @param server_port specifies port number to listen on /// @param client_port specifies port number to send to + /// @param run_multithreaded enables or disables multithreaded mode /// @param use_bcast configure sockets to support broadcast messages. /// @param direct_response_desired specifies if it is desired to /// use direct V4 traffic. Dhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT, uint16_t client_port = 0, + bool run_multithreaded = false, const bool use_bcast = true, const bool direct_response_desired = true); /// @brief Destructor. Used during DHCPv4 service shutdown. virtual ~Dhcpv4Srv(); - /// @brief Checks if the server is running in a test mode. + /// @brief Checks if the server is running in unit test mode. /// - /// @return true if the server is running in the test mode, + /// @return true if the server is running in unit test mode, /// false otherwise. bool inTestMode() const { return (server_port_ == 0); @@ -265,6 +269,12 @@ public: /// redeclaration/redefinition. @ref isc::process::Daemon::getVersion() static std::string getVersion(bool extended); + /// @brief returns Kea DHCPv4 server thread count. + static uint32_t threadCount(); + + /// @brief returns Kea DHCPv4 server max thread queue size. + static uint32_t maxThreadQueueSize(); + /// @brief Main server processing loop. /// /// Main server processing loop. Call the processing step routine @@ -280,6 +290,24 @@ public: /// a response. void run_one(); + /// @brief Process a single incoming DHCPv4 packet and sends the response. + /// + /// It verifies correctness of the passed packet, call per-type processXXX + /// methods, generates appropriate answer, sends the answer to the client. + /// + /// @param query A pointer to the packet to be processed. + /// @param rsp A pointer to the response + void processPacketAndSendResponse(Pkt4Ptr& query, Pkt4Ptr& rsp); + + /// @brief Process a single incoming DHCPv4 packet and sends the response. + /// + /// It verifies correctness of the passed packet, call per-type processXXX + /// methods, generates appropriate answer, sends the answer to the client. + /// + /// @param query A pointer to the packet to be processed. + /// @param rsp A pointer to the response + void processPacketAndSendResponseNoThrow(Pkt4Ptr& query, Pkt4Ptr& rsp); + /// @brief Process a single incoming DHCPv4 packet. /// /// It verifies correctness of the passed packet, call per-type processXXX @@ -351,7 +379,9 @@ public: NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr& ncr); - /// @brief Discard all in-progress packets + /// @brief Discards cached and parked packets + /// Clears the call_handle store and packet parking lots + /// of all packets. Called during reconfigure and shutdown. void discardPackets(); protected: @@ -585,7 +615,7 @@ protected: /// @param lease lease being assigned to the client /// @param subnet the subnet to which the lease belongs /// @param resp outbound response for the client to which timers are added. - void setTeeTimes(const Lease4Ptr& lease, const Subnet4Ptr& subnet, Pkt4Ptr resp); + static void setTeeTimes(const Lease4Ptr& lease, const Subnet4Ptr& subnet, Pkt4Ptr resp); /// @brief Append basic options if they are not present. /// @@ -879,10 +909,6 @@ protected: bool& drop, bool sanity_only = false) const; - /// indicates if shutdown is in progress. Setting it to true will - /// initiate server shutdown procedure. - volatile bool shutdown_; - /// @brief dummy wrapper around IfaceMgr::receive4 /// /// This method is useful for testing purposes, where its replacement @@ -908,7 +934,6 @@ protected: void classifyPacket(const Pkt4Ptr& pkt); public: - /// @brief Evaluate classes. /// /// @note Second part of the classification. @@ -961,11 +986,6 @@ protected: void processPacketBufferSend(hooks::CalloutHandlePtr& callout_handle, Pkt4Ptr& rsp); - /// @brief Allocation Engine. - /// Pointer to the allocation engine that we are currently using - /// It must be a pointer, because we will support changing engines - /// during normal operation (e.g. to use different allocators) - boost::shared_ptr alloc_engine_; private: @@ -984,17 +1004,25 @@ private: /// @return Option that contains netmask information static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet); - /// Should broadcast be enabled on sockets (if true). - bool use_bcast_; - protected: - /// UDP port number on which server listens. uint16_t server_port_; - /// UDP port number to which server sends responses. + /// UDP port number to which server sends all responses. uint16_t client_port_; + /// Should broadcast be enabled on sockets (if true). + bool use_bcast_; + + /// Indicates if shutdown is in progress. Setting it to true will + /// initiate server shutdown procedure. + volatile bool shutdown_; + + /// @brief Allocation Engine. + /// Pointer to the allocation engine that we are currently using + /// It must be a pointer, because we will support changing engines + /// during normal operation (e.g. to use different allocators) + boost::shared_ptr alloc_engine_; /// @brief Holds information about disabled DHCP service and/or /// disabled subnet/network scopes. NetworkStatePtr network_state_; @@ -1002,6 +1030,14 @@ protected: /// @brief Controls access to the configuration backends. CBControlDHCPv4Ptr cb_control_; + /// @brief Packet processing thread pool + ThreadPool pkt_thread_pool_; + + // Specifies if the application will use a thread pool or will process + // received DHCP packets on the main thread. + // It is mandatory to be set on false when running the test cases. + std::atomic_bool run_multithreaded_; + public: /// Class methods for DHCPv4-over-DHCPv6 handler @@ -1042,7 +1078,7 @@ public: static int getHookIndexLease4Decline(); }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif // DHCP4_SRV_H diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc index a6b70ef803..c817f3f097 100644 --- a/src/bin/dhcp4/main.cc +++ b/src/bin/dhcp4/main.cc @@ -7,15 +7,17 @@ #include #include +#include #include #include #include #include -#include #include #include #include +#include #include +#include #include @@ -31,6 +33,9 @@ using namespace std; /// instantiates ControlledDhcpv4Srv class that is responsible for establishing /// connection with msgq (receiving commands and configuration) and also /// creating Dhcpv4 server object as well. +/// +/// For detailed explanation or relations between main(), ControlledDhcpv4Srv, +/// Dhcpv4Srv and other classes, see \ref dhcpv4Session. namespace { @@ -57,7 +62,7 @@ usage() { << "(useful for testing only)" << endl; exit(EXIT_FAILURE); } -} // end of anonymous namespace +} // namespace int main(int argc, char* argv[]) { @@ -98,7 +103,7 @@ main(int argc, char* argv[]) { config_file = optarg; break; - case 'p': + case 'p': // server port number try { server_port_number = boost::lexical_cast(optarg); } catch (const boost::bad_lexical_cast &) { @@ -113,7 +118,7 @@ main(int argc, char* argv[]) { } break; - case 'P': + case 'P': // client port number try { client_port_number = boost::lexical_cast(optarg); } catch (const boost::bad_lexical_cast &) { @@ -138,7 +143,6 @@ main(int argc, char* argv[]) { usage(); } - // Configuration file is required. if (config_file.empty()) { cerr << "Configuration file not specified." << endl; @@ -150,7 +154,6 @@ main(int argc, char* argv[]) { if (check_mode) { try { - // We need to initialize logging, in case any error messages are to be printed. // This is just a test, so we don't care about lockfile. setenv("KEA_LOCKFILE_DIR", "none", 0); @@ -214,7 +217,7 @@ main(int argc, char* argv[]) { LOG_INFO(dhcp4_logger, DHCP4_STARTING).arg(VERSION); // Create the server instance. - ControlledDhcpv4Srv server(server_port_number, client_port_number); + ControlledDhcpv4Srv server(server_port_number, client_port_number, true); // Remember verbose-mode server.setVerbose(verbose_mode); diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 029055ac3d..7b2f7ef385 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -21,10 +21,10 @@ #include #include #include -#include #include #include #include +#include #include "marker_file.h" #include "test_libraries.h" @@ -535,115 +535,6 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelShutdown) { EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response); } -// Tests that the server properly responds to statistics commands. Note this -// is really only intended to verify that the appropriate Statistics handler -// is called based on the command. It is not intended to be an exhaustive -// test of Dhcpv4 statistics. -TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) { - createUnixChannelServer(); - std::string response; - - // Check statistic-get - sendUnixCommand("{ \"command\" : \"statistic-get\", " - " \"arguments\": {" - " \"name\":\"bogus\" }}", response); - EXPECT_EQ("{ \"arguments\": { }, \"result\": 0 }", response); - - // Check statistic-get-all - sendUnixCommand("{ \"command\" : \"statistic-get-all\", " - " \"arguments\": {}}", response); - - std::set initial_stats = { - "pkt4-received", - "pkt4-discover-received", - "pkt4-offer-received", - "pkt4-request-received", - "pkt4-ack-received", - "pkt4-nak-received", - "pkt4-release-received", - "pkt4-decline-received", - "pkt4-inform-received", - "pkt4-unknown-received", - "pkt4-sent", - "pkt4-offer-sent", - "pkt4-ack-sent", - "pkt4-nak-sent", - "pkt4-parse-failed", - "pkt4-receive-drop" - }; - - // preparing the schema which check if all statistics are set to zero - std::ostringstream s; - s << "{ \"arguments\": { "; - for (auto st = initial_stats.begin(); st != initial_stats.end();) { - s << "\"" << *st << "\": [ [ 0, \""; - s << isc::util::ptimeToText(StatsMgr::instance().getObservation(*st)->getInteger().second); - s << "\" ] ]"; - if (++st != initial_stats.end()) { - s << ", "; - } - } - s << " }, \"result\": 0 }"; - - auto stats_get_all = s.str(); - - EXPECT_EQ(stats_get_all, response); - - // Check statistic-reset - sendUnixCommand("{ \"command\" : \"statistic-reset\", " - " \"arguments\": {" - " \"name\":\"bogus\" }}", response); - EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", - response); - - // Check statistic-reset-all - sendUnixCommand("{ \"command\" : \"statistic-reset-all\", " - " \"arguments\": {}}", response); - EXPECT_EQ("{ \"result\": 0, \"text\": " - "\"All statistics reset to neutral values.\" }", response); - - // Check statistic-remove - sendUnixCommand("{ \"command\" : \"statistic-remove\", " - " \"arguments\": {" - " \"name\":\"bogus\" }}", response); - EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", - response); - - // Check statistic-remove-all - sendUnixCommand("{ \"command\" : \"statistic-remove-all\", " - " \"arguments\": {}}", response); - EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }", - response); - - // Check statistic-sample-age-set - sendUnixCommand("{ \"command\" : \"statistic-sample-age-set\", " - " \"arguments\": {" - " \"name\":\"bogus\", \"duration\": 1245 }}", response); - EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", - response); - - // Check statistic-sample-age-set-all - sendUnixCommand("{ \"command\" : \"statistic-sample-age-set-all\", " - " \"arguments\": {" - " \"duration\": 1245 }}", response); - EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics duration limit are set.\" }", - response); - - // Check statistic-sample-count-set - sendUnixCommand("{ \"command\" : \"statistic-sample-count-set\", " - " \"arguments\": {" - " \"name\":\"bogus\", \"max-samples\": 100 }}", response); - EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", - response); - - // Check statistic-sample-count-set-all - sendUnixCommand("{ \"command\" : \"statistic-sample-count-set-all\", " - " \"arguments\": {" - " \"max-samples\": 100 }}", response); - EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics count limit are set.\" }", - response); -} - // Check that the "config-set" command will replace current configuration TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { createUnixChannelServer(); @@ -1136,6 +1027,115 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) { ASSERT_FALSE(lease1); } +// Tests that the server properly responds to statistics commands. Note this +// is really only intended to verify that the appropriate Statistics handler +// is called based on the command. It is not intended to be an exhaustive +// test of Dhcpv4 statistics. +TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) { + createUnixChannelServer(); + std::string response; + + // Check statistic-get + sendUnixCommand("{ \"command\" : \"statistic-get\", " + " \"arguments\": {" + " \"name\":\"bogus\" }}", response); + EXPECT_EQ("{ \"arguments\": { }, \"result\": 0 }", response); + + // Check statistic-get-all + sendUnixCommand("{ \"command\" : \"statistic-get-all\", " + " \"arguments\": {}}", response); + + std::set initial_stats = { + "pkt4-received", + "pkt4-discover-received", + "pkt4-offer-received", + "pkt4-request-received", + "pkt4-ack-received", + "pkt4-nak-received", + "pkt4-release-received", + "pkt4-decline-received", + "pkt4-inform-received", + "pkt4-unknown-received", + "pkt4-sent", + "pkt4-offer-sent", + "pkt4-ack-sent", + "pkt4-nak-sent", + "pkt4-parse-failed", + "pkt4-receive-drop" + }; + + // preparing the schema which check if all statistics are set to zero + std::ostringstream s; + s << "{ \"arguments\": { "; + for (auto st = initial_stats.begin(); st != initial_stats.end();) { + s << "\"" << *st << "\": [ [ 0, \""; + s << isc::util::ptimeToText(StatsMgr::instance().getObservation(*st)->getInteger().second); + s << "\" ] ]"; + if (++st != initial_stats.end()) { + s << ", "; + } + } + s << " }, \"result\": 0 }"; + + auto stats_get_all = s.str(); + + EXPECT_EQ(stats_get_all, response); + + // Check statistic-reset + sendUnixCommand("{ \"command\" : \"statistic-reset\", " + " \"arguments\": {" + " \"name\":\"bogus\" }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); + + // Check statistic-reset-all + sendUnixCommand("{ \"command\" : \"statistic-reset-all\", " + " \"arguments\": {}}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": " + "\"All statistics reset to neutral values.\" }", response); + + // Check statistic-remove + sendUnixCommand("{ \"command\" : \"statistic-remove\", " + " \"arguments\": {" + " \"name\":\"bogus\" }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); + + // Check statistic-remove-all + sendUnixCommand("{ \"command\" : \"statistic-remove-all\", " + " \"arguments\": {}}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }", + response); + + // Check statistic-sample-age-set + sendUnixCommand("{ \"command\" : \"statistic-sample-age-set\", " + " \"arguments\": {" + " \"name\":\"bogus\", \"duration\": 1245 }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); + + // Check statistic-sample-age-set-all + sendUnixCommand("{ \"command\" : \"statistic-sample-age-set-all\", " + " \"arguments\": {" + " \"duration\": 1245 }}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics duration limit are set.\" }", + response); + + // Check statistic-sample-count-set + sendUnixCommand("{ \"command\" : \"statistic-sample-count-set\", " + " \"arguments\": {" + " \"name\":\"bogus\", \"max-samples\": 100 }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); + + // Check statistic-sample-count-set-all + sendUnixCommand("{ \"command\" : \"statistic-sample-count-set-all\", " + " \"arguments\": {" + " \"max-samples\": 100 }}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics count limit are set.\" }", + response); +} + // Tests that the server properly responds to shtudown command sent // via ControlChannel TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) { diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 5146443061..f88a55dd38 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -5,30 +5,38 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include -#include + #include +#include +#include #include +#include #include -#include -#include #include #include #include #include #include +#include +#include +#include #include #include #include -#include +#include + #include + +#include #include using namespace isc::config; -using namespace isc::db; using namespace isc::data; +using namespace isc::db; using namespace isc::dhcp; using namespace isc::hooks; using namespace isc::stats; +using namespace isc::util::thread; using namespace std; namespace { @@ -96,8 +104,6 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) { // configuration from a JSON file. isc::data::ConstElementPtr json; - isc::data::ConstElementPtr dhcp6; - isc::data::ConstElementPtr logger; isc::data::ConstElementPtr result; // Basic sanity check: file name must not be empty. @@ -139,8 +145,7 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) { // Now check is the returned result is successful (rcode=0) or not // (see @ref isc::config::parseAnswer). int rcode; - isc::data::ConstElementPtr comment = - isc::config::parseAnswer(rcode, result); + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); if (rcode != 0) { string reason = comment ? comment->stringValue() : "no details available"; @@ -160,11 +165,11 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) { return (result); } - void ControlledDhcpv6Srv::init(const std::string& file_name) { // Configure the server using JSON file. ConstElementPtr result = loadConfigFile(file_name); + int rcode; ConstElementPtr comment = isc::config::parseAnswer(rcode, result); if (rcode != 0) { @@ -189,17 +194,16 @@ void ControlledDhcpv6Srv::cleanup() { // Nothing to do here. No need to disconnect from anything. } - ConstElementPtr ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr) { - if (ControlledDhcpv6Srv::server_) { - ControlledDhcpv6Srv::server_->shutdown(); + if (ControlledDhcpv6Srv::getInstance()) { + ControlledDhcpv6Srv::getInstance()->shutdown(); } else { LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING); - ConstElementPtr answer = isc::config::createAnswer(1, "Shutdown failure."); + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure."); return (answer); } - ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down."); + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down."); return (answer); } @@ -211,11 +215,11 @@ ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) { bool status = HooksManager::loadLibraries(loaded); if (!status) { LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL); - ConstElementPtr answer = isc::config::createAnswer(1, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Failed to reload hooks libraries."); return (answer); } - ConstElementPtr answer = isc::config::createAnswer(0, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Hooks libraries successfully reloaded."); return (answer); } @@ -494,7 +498,7 @@ ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) { ElementPtr extended = Element::create(Dhcpv6Srv::getVersion(true)); ElementPtr arguments = Element::createMap(); arguments->set("extended", extended); - ConstElementPtr answer = isc::config::createAnswer(0, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, Dhcpv6Srv::getVersion(false), arguments); return (answer); @@ -503,14 +507,14 @@ ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) { ConstElementPtr ControlledDhcpv6Srv::commandBuildReportHandler(const string&, ConstElementPtr) { ConstElementPtr answer = - isc::config::createAnswer(0, isc::detail::getConfigReport()); + isc::config::createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport()); return (answer); } ConstElementPtr ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&, ConstElementPtr args) { - int status_code = 1; + int status_code = CONTROL_RESULT_ERROR; string message; // args must be { "remove": } @@ -574,9 +578,9 @@ ControlledDhcpv6Srv::commandConfigBackendPullHandler(const std::string&, "On demand configuration update successful.")); } -isc::data::ConstElementPtr -ControlledDhcpv6Srv::processCommand(const std::string& command, - isc::data::ConstElementPtr args) { +ConstElementPtr +ControlledDhcpv6Srv::processCommand(const string& command, + ConstElementPtr args) { string txt = args ? args->str() : "(none)"; LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED) @@ -585,12 +589,17 @@ ControlledDhcpv6Srv::processCommand(const std::string& command, ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); if (!srv) { - ConstElementPtr no_srv = isc::config::createAnswer(1, + ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Server object not initialized, can't process command '" + command + "', arguments: '" + txt + "'."); return (no_srv); } + if (srv->run_multithreaded_) { + srv->pkt_thread_pool_.destroy(); + srv->pkt_thread_pool_.create(Dhcpv6Srv::threadCount()); + } + try { if (command == "shutdown") { return (srv->commandShutdownHandler(command, args)); @@ -635,13 +644,12 @@ ControlledDhcpv6Srv::processCommand(const std::string& command, return (srv->commandConfigBackendPullHandler(command, args)); } - - return (isc::config::createAnswer(1, "Unrecognized command:" - + command)); - + return(isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unrecognized command: " + + command)); } catch (const Exception& ex) { - return (isc::config::createAnswer(1, "Error while processing command '" - + command + "':" + ex.what())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Error while processing command '" + + command + "': " + ex.what() + + ", params: '" + txt + "'")); } } @@ -653,11 +661,12 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); + // Single stream instance used in all error clauses + std::ostringstream err; + if (!srv) { - ConstElementPtr no_srv = isc::config::createAnswer( - CONTROL_RESULT_ERROR, - "Server object not initialized, can't process config."); - return (no_srv); + err << "Server object not initialized, can't process config."; + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } ConstElementPtr answer = configureDhcp6Server(*srv, config); @@ -671,8 +680,8 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Failed to process configuration:" - + string(ex.what()))); + err << "Failed to process configuration: " << ex.what(); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Re-open lease and host database with new parameters. @@ -683,8 +692,8 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { cfg_db->setAppendedParameters("universe=6"); cfg_db->createManagers(); } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Unable to open database: " - + std::string(ex.what()))); + err << "Unable to open database: " << ex.what(); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Regenerate server identifier if needed. @@ -703,17 +712,16 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { } catch (const std::exception& ex) { std::ostringstream err; err << "unable to configure server identifier: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Server will start DDNS communications if its enabled. try { srv->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())); + err << "Error starting DHCP_DDNS client after server reconfiguration: " + << ex.what(); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Setup DHCPv4-over-DHCPv6 IPC @@ -723,7 +731,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { std::ostringstream err; err << "error starting DHCPv4-over-DHCPv6 IPC " " after server reconfiguration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Configure DHCP packet queueing @@ -736,7 +744,6 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { } } catch (const std::exception& ex) { - std::ostringstream err; err << "Error setting packet queue controls after server reconfiguration: " << ex.what(); return (isc::config::createAnswer(1, err.str())); @@ -760,11 +767,10 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { server_); } catch (const std::exception& ex) { - std::ostringstream err; err << "unable to setup timers for periodically running the" " reclamation of the expired leases: " << ex.what() << "."; - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Setup config backend polling, if configured for it. @@ -830,24 +836,27 @@ ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) { ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); + // Single stream instance used in all error clauses + std::ostringstream err; + if (!srv) { - ConstElementPtr no_srv = isc::config::createAnswer(1, - "Server object not initialized, can't process config."); - return (no_srv); + err << "Server object not initialized, can't process config."; + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } return (configureDhcp6Server(*srv, config, true)); } ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port, - uint16_t client_port) - : Dhcpv6Srv(server_port, client_port), io_service_(), + uint16_t client_port, + bool run_multithreaded /*= false*/) + : Dhcpv6Srv(server_port, client_port, run_multithreaded), io_service_(), timer_mgr_(TimerMgr::instance()) { - if (server_) { + if (getInstance()) { isc_throw(InvalidOperation, "There is another Dhcpv6Srv instance already."); } - server_ = this; // remember this instance for use in callback + server_ = this; // remember this instance for later use in handlers // TimerMgr uses IO service to run asynchronous timers. TimerMgr::instance()->setIOService(getIOService()); @@ -869,17 +878,23 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port, CommandMgr::instance().registerCommand("config-reload", boost::bind(&ControlledDhcpv6Srv::commandConfigReloadHandler, this, _1, _2)); + CommandMgr::instance().registerCommand("config-set", + boost::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, _1, _2)); + CommandMgr::instance().registerCommand("config-test", boost::bind(&ControlledDhcpv6Srv::commandConfigTestHandler, this, _1, _2)); CommandMgr::instance().registerCommand("config-write", boost::bind(&ControlledDhcpv6Srv::commandConfigWriteHandler, this, _1, _2)); + CommandMgr::instance().registerCommand("dhcp-enable", + boost::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, 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("libreload", + boost::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, _1, _2)); CommandMgr::instance().registerCommand("leases-reclaim", boost::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, _1, _2)); @@ -887,12 +902,6 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port, CommandMgr::instance().registerCommand("server-tag-get", boost::bind(&ControlledDhcpv6Srv::commandServerTagGetHandler, this, _1, _2)); - CommandMgr::instance().registerCommand("libreload", - boost::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, _1, _2)); - - CommandMgr::instance().registerCommand("config-set", - boost::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, _1, _2)); - CommandMgr::instance().registerCommand("shutdown", boost::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, _1, _2)); @@ -988,8 +997,8 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { void ControlledDhcpv6Srv::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(); + if (getInstance()) { + getInstance()->io_service_.run_one(); } } @@ -1028,12 +1037,13 @@ ControlledDhcpv6Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { if (reopened) { // Cancel the timer. if (TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) { - TimerMgr::instance()->cancel("Dhcp6DbReconnectTimer"); } + TimerMgr::instance()->cancel("Dhcp6DbReconnectTimer"); + } // Set network state to service enabled network_state_->enableService(); - // Toss the reconnct control, we're done with it + // Toss the reconnect control, we're done with it db_reconnect_ctl.reset(); } else { if (!db_reconnect_ctl->checkRetries()) { @@ -1051,7 +1061,7 @@ ControlledDhcpv6Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { if (!TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) { TimerMgr::instance()->registerTimer("Dhcp6DbReconnectTimer", boost::bind(&ControlledDhcpv6Srv::dbReconnect, this, - db_reconnect_ctl), + db_reconnect_ctl), db_reconnect_ctl->retryInterval(), asiolink::IntervalTimer::ONE_SHOT); } @@ -1116,5 +1126,5 @@ ControlledDhcpv6Srv::cbFetchUpdates(const SrvConfigPtr& srv_cfg, } } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h index fbe7252337..cbfeb38d09 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.h +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h @@ -29,8 +29,10 @@ public: /// /// @param server_port UDP port to be opened for DHCP traffic /// @param client_port UDP port where all responses are sent to. + /// @param run_multithreaded enables or disables multithreaded mode ControlledDhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT, - uint16_t client_port = 0); + uint16_t client_port = 0, + bool run_multithreaded = false); /// @brief Destructor. virtual ~ControlledDhcpv6Srv(); @@ -65,7 +67,7 @@ public: /// @brief Initiates shutdown procedure for the whole DHCPv6 server. void shutdown(); - /// @brief command processor + /// @brief Command processor /// /// This method is uniform for all config backends. It processes received /// command (as a string + JSON arguments). Internally, it's just a @@ -75,9 +77,9 @@ public: /// Currently supported commands are: /// - config-reload /// - config-test - /// - leases-reclaim - /// - libreload /// - shutdown + /// - libreload + /// - leases-reclaim /// ... /// /// @note It never throws. @@ -89,7 +91,7 @@ public: static isc::data::ConstElementPtr processCommand(const std::string& command, isc::data::ConstElementPtr args); - /// @brief configuration processor + /// @brief Configuration processor /// /// This is a method for handling incoming configuration updates. /// This method should be called by all configuration backends when the @@ -114,7 +116,7 @@ public: isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr new_config); - /// @brief returns pointer to the sole instance of Dhcpv6Srv + /// @brief Returns pointer to the sole instance of Dhcpv6Srv /// /// @return server instance (may return NULL, if called before server is spawned) static ControlledDhcpv6Srv* getInstance() { @@ -122,7 +124,6 @@ public: } private: - /// @brief Callback that will be called from iface_mgr when data /// is received over control socket. /// @@ -131,7 +132,7 @@ private: /// (that was sent from some yet unspecified sender). static void sessionReader(void); - /// @brief handler for processing 'shutdown' command + /// @brief Handler for processing 'shutdown' command /// /// This handler processes shutdown command, which initializes shutdown /// procedure. @@ -143,7 +144,7 @@ private: commandShutdownHandler(const std::string& command, isc::data::ConstElementPtr args); - /// @brief handler for processing 'libreload' command + /// @brief Handler for processing 'libreload' command /// /// This handler processes libreload command, which unloads all hook /// libraries and reloads them. @@ -156,7 +157,7 @@ private: commandLibReloadHandler(const std::string& command, isc::data::ConstElementPtr args); - /// @brief handler for processing 'config-reload' command + /// @brief Handler for processing 'config-reload' command /// /// This handler processes config-reload command, which processes /// configuration specified in args parameter. @@ -336,7 +337,6 @@ private: const bool remove_lease, const uint16_t max_unwarned_cycles); - /// @brief Deletes reclaimed leases and reschedules the timer. /// /// This is a wrapper method for @c AllocEngine::deleteExpiredReclaimed6. @@ -361,6 +361,7 @@ private: /// /// If the maximum number of retries has been exhausted an error is logged /// and the server shuts down. + /// /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the /// configured reconnect parameters /// @@ -382,6 +383,8 @@ private: /// /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the /// configured reconnect parameters + /// + /// @return false if reconnect is not configured, true otherwise bool dbLostCallback(db::ReconnectCtlPtr db_reconnect_ctl); /// @brief Callback invoked periodically to fetch configuration updates @@ -414,7 +417,7 @@ private: TimerMgrPtr timer_mgr_; }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index d1e02821ae..116fe3cc76 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -45,11 +45,11 @@ #include #include #include - #include #include #include #include +#include #include #include #include @@ -91,6 +91,8 @@ using namespace isc::stats; using namespace isc::util; using namespace std; +using isc::util::thread::LockGuard; + namespace { /// Structure that holds registered hook indexes @@ -197,19 +199,23 @@ std::set dhcp6_statistics = { "pkt6-receive-drop" }; -}; // anonymous namespace +} // namespace namespace isc { namespace dhcp { const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_"); -Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port) +Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port, + bool run_multithreaded /* = false */) : io_service_(new IOService()), server_port_(server_port), - client_port_(client_port), serverid_(), shutdown_(true), - alloc_engine_(), name_change_reqs_(), + client_port_(client_port), serverid_(), + shutdown_(true), alloc_engine_(), + name_change_reqs_(), network_state_(new NetworkState(NetworkState::DHCPv6)), - cb_control_(new CBControlDHCPv6()) { + cb_control_(new CBControlDHCPv6()), + run_multithreaded_(run_multithreaded) { + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET) .arg(server_port); @@ -442,6 +448,14 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt, } bool Dhcpv6Srv::run() { + if (run_multithreaded_) { + // Creating the process packet thread pool + // The number of thread pool's threads should be read from configuration + // file or it should be determined by the number of hardware threads and + // the number of Cassandra DB nodes. + pkt_thread_pool_.create(Dhcpv6Srv::threadCount()); + } + while (!shutdown_) { try { run_one(); @@ -458,6 +472,11 @@ bool Dhcpv6Srv::run() { } } + // destroying the thread pool + if (run_multithreaded_) { + pkt_thread_pool_.destroy(); + } + return (true); } @@ -467,6 +486,18 @@ void Dhcpv6Srv::run_one() { Pkt6Ptr rsp; try { + + // Do not read more packets from socket if there are enough + // packets to be processed in the packet thread pool queue + const int max_queued_pkt_per_thread = Dhcpv6Srv::maxThreadQueueSize(); + const auto queue_full_wait = std::chrono::milliseconds(1); + size_t pkt_queue_size = pkt_thread_pool_.count(); + if (pkt_queue_size >= Dhcpv6Srv::threadCount() * + max_queued_pkt_per_thread) { + std::this_thread::sleep_for(queue_full_wait); + return; + } + // Set select() timeout to 1s. This value should not be modified // because it is important that the select() returns control // frequently so as the IOService can be polled for ready handlers. @@ -544,9 +575,31 @@ void Dhcpv6Srv::run_one() { .arg(query->getLabel()); return; } else { - processPacket(query, rsp); + if (run_multithreaded_) { + ThreadPool::WorkItemCallBack call_back = + std::bind(&Dhcpv6Srv::processPacketAndSendResponseNoThrow, this, query, rsp); + pkt_thread_pool_.add(call_back); + } else { + processPacketAndSendResponse(query, rsp); + } } +} +void +Dhcpv6Srv::processPacketAndSendResponseNoThrow(Pkt6Ptr& query, Pkt6Ptr& rsp) { + try { + processPacketAndSendResponse(query, rsp); + } catch (const std::exception& e) { + LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_STD_EXCEPTION) + .arg(e.what()); + } catch (...) { + LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_EXCEPTION); + } +} + +void +Dhcpv6Srv::processPacketAndSendResponse(Pkt6Ptr& query, Pkt6Ptr& rsp) { + processPacket(query, rsp); if (!rsp) { return; } @@ -1350,7 +1403,7 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt) { switch (pkt->getType()) { case DHCPV6_SOLICIT: case DHCPV6_REBIND: - case DHCPV6_CONFIRM: + case DHCPV6_CONFIRM: sanityCheck(pkt, MANDATORY, FORBIDDEN); return (true); @@ -1573,6 +1626,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer, if (answer_opt) { answer->addOption(answer_opt); } + break; } default: break; @@ -3977,6 +4031,23 @@ Dhcpv6Srv::requestedInORO(const Pkt6Ptr& query, const uint16_t code) const { return (false); } +uint32_t Dhcpv6Srv::threadCount() { + uint32_t sys_threads = CfgMgr::instance().getCurrentCfg()->getServerThreadCount(); + if (sys_threads) { + return sys_threads; + } + sys_threads = std::thread::hardware_concurrency(); + return sys_threads * 1; +} + +uint32_t Dhcpv6Srv::maxThreadQueueSize() { + uint32_t max_thread_queue_size = CfgMgr::instance().getCurrentCfg()->getServerMaxThreadQueueSize(); + if (max_thread_queue_size) { + return max_thread_queue_size; + } + return 4; +} + void Dhcpv6Srv::discardPackets() { // Dump all of our current packets, anything that is mid-stream isc::dhcp::Pkt6Ptr pkt6ptr_empty; @@ -4020,5 +4091,5 @@ Dhcpv6Srv::setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr& subnet, Option6 } } -}; -}; +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 80e42a47b1..9d4348a51a 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -8,14 +8,16 @@ #define DHCPV6_SRV_H #include -#include #include +#include #include #include +#include #include #include -#include -#include +#include +#include +#include #include #include #include @@ -26,9 +28,13 @@ #include #include +#include + #include #include #include +#include +#include // Undefine the macro OPTIONAL which is defined in some operating // systems but conflicts with a member of the RequirementLevel enum in @@ -51,12 +57,16 @@ public: /// @brief DHCPv6 server service. /// -/// This class represents DHCPv6 server. It contains all +/// This singleton class represents DHCPv6 server. It contains all /// top-level methods and routines necessary for server operation. /// In particular, it instantiates IfaceMgr, loads or generates DUID /// that is going to be used as server-identifier, receives incoming /// packets, processes them, manages leases assignment and generates /// appropriate responses. +/// +/// This class does not support any controlling mechanisms directly. +/// See the derived \ref ControlledDhcpv6Srv class for support for +/// command and configuration updates over msgq. class Dhcpv6Srv : public process::Daemon { private: @@ -79,12 +89,17 @@ public: /// Instantiates necessary services, required to run DHCPv6 server. /// In particular, creates IfaceMgr that will be responsible for /// network interaction. Will instantiate lease manager, and load - /// old or create new DUID. - /// - /// @param server_port port on which all sockets will listen - /// @param client_port port to which all responses will be sent + /// old or create new DUID. It is possible to specify alternate + /// port on which DHCPv6 server will listen on and alternate port + /// where DHCPv6 server sends all responses to. Those are mostly useful + /// for testing purposes. + /// + /// @param server_port specifies port number to listen on + /// @param client_port specifies port number to send to + /// @param run_multithreaded enables or disables multithreaded mode Dhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT, - uint16_t client_port = 0); + uint16_t client_port = 0, + bool run_multithreaded = false); /// @brief Destructor. Used during DHCPv6 service shutdown. virtual ~Dhcpv6Srv(); @@ -120,6 +135,12 @@ public: /// redeclaration/redefinition. @ref isc::process::Daemon::getVersion() static std::string getVersion(bool extended); + /// @brief returns Kea DHCPv6 server thread count. + static uint32_t threadCount(); + + /// @brief returns Kea DHCPv6 server max thread queue size. + static uint32_t maxThreadQueueSize(); + /// @brief Returns server-identifier option. /// /// @return server-id option @@ -140,6 +161,24 @@ public: /// a response. void run_one(); + /// @brief Process a single incoming DHCPv6 packet and sends the response. + /// + /// It verifies correctness of the passed packet, call per-type processXXX + /// methods, generates appropriate answer, sends the answer to the client. + /// + /// @param query A pointer to the packet to be processed. + /// @param rsp A pointer to the response + void processPacketAndSendResponse(Pkt6Ptr& query, Pkt6Ptr& rsp); + + /// @brief Process a single incoming DHCPv6 packet and sends the response. + /// + /// It verifies correctness of the passed packet, call per-type processXXX + /// methods, generates appropriate answer, sends the answer to the client. + /// + /// @param query A pointer to the packet to be processed. + /// @param rsp A pointer to the response + void processPacketAndSendResponseNoThrow(Pkt6Ptr& query, Pkt6Ptr& rsp); + /// @brief Process a single incoming DHCPv6 packet. /// /// It verifies correctness of the passed packet, call per-type processXXX @@ -152,15 +191,21 @@ public: /// @brief Instructs the server to shut down. void shutdown(); + /// + /// @name Public accessors returning values required to (re)open sockets. + /// + //@{ + /// /// @brief Get UDP port on which server should listen. /// - /// Typically, server listens on UDP port 547. Other ports are only - /// used for testing purposes. + /// Typically, server listens on UDP port 547. Other ports are used + /// for testing purposes only. /// /// @return UDP port on which server should listen. uint16_t getServerPort() const { return (server_port_); } + //@} /// @brief Starts DHCP_DDNS client IO if DDNS updates are enabled. /// @@ -714,7 +759,7 @@ protected: /// @param preferred_lft preferred lease time of the lease being assigned to the client /// @param subnet the subnet to which the lease belongs /// @param resp outbound IA option in which the timers are set. - void setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr& subnet, Option6IAPtr& resp); + static void setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr& subnet, Option6IAPtr& resp); /// @brief Attempts to release received addresses /// @@ -761,6 +806,7 @@ protected: /// @param pkt packet to be classified void classifyPacket(const Pkt6Ptr& pkt); +public: /// @brief Evaluate classes. /// /// @note Second part of the classification. @@ -771,7 +817,7 @@ protected: /// @param pkt packet to be classified. /// @param depend_on_known if false classes depending on the KNOWN or /// UNKNOWN classes are skipped, if true only these classes are evaluated. - void evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known); + static void evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known); /// @brief Assigns classes retrieved from host reservation database. /// @@ -1057,9 +1103,17 @@ protected: /// @brief Controls access to the configuration backends. CBControlDHCPv6Ptr cb_control_; + + /// @brief Packet processing thread pool + ThreadPool pkt_thread_pool_; + + // Specifies if the application will use a thread pool or will process + // received DHCP packets on the main thread. + // It is mandatory to be set on false when running the test cases. + std::atomic_bool run_multithreaded_; }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif // DHCP6_SRV_H diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc index 42946db9c6..28c3e8ceea 100644 --- a/src/bin/dhcp6/main.cc +++ b/src/bin/dhcp6/main.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -37,9 +38,8 @@ using namespace std; /// Dhcpv6Srv and other classes, see \ref dhcpv6Session. namespace { -const char* const DHCP6_NAME = "kea-dhcp6"; -const char* const DHCP6_LOGGER_NAME = "kea-dhcp6"; +const char* const DHCP6_NAME = "kea-dhcp6"; /// @brief Prints Kea Usage and exits /// @@ -62,7 +62,7 @@ usage() { << "(useful for testing only)" << endl; exit(EXIT_FAILURE); } -} // end of anonymous namespace +} // namespace int main(int argc, char* argv[]) { @@ -193,11 +193,8 @@ main(int argc, char* argv[]) { cerr << "Error encountered: " << answer->stringValue() << endl; return (EXIT_FAILURE); } - - - return (EXIT_SUCCESS); } catch (const std::exception& ex) { - cerr << "Syntax check failed with " << ex.what() << endl; + cerr << "Syntax check failed with: " << ex.what() << endl; } return (EXIT_FAILURE); } @@ -207,11 +204,10 @@ main(int argc, char* argv[]) { // It is important that we set a default logger name because this name // will be used when the user doesn't provide the logging configuration // in the Kea configuration file. - Daemon::setDefaultLoggerName(DHCP6_LOGGER_NAME); + Daemon::setDefaultLoggerName(DHCP6_ROOT_LOGGER_NAME); // Initialize logging. If verbose, we'll use maximum verbosity. - Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode); - + Daemon::loggerInit(DHCP6_ROOT_LOGGER_NAME, verbose_mode); LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO) .arg(getpid()) .arg(server_port_number) @@ -221,21 +217,19 @@ main(int argc, char* argv[]) { LOG_INFO(dhcp6_logger, DHCP6_STARTING).arg(VERSION); // Create the server instance. - ControlledDhcpv6Srv server(server_port_number, client_port_number); + ControlledDhcpv6Srv server(server_port_number, client_port_number, true); // Remember verbose-mode server.setVerbose(verbose_mode); - // Create our PID file + // Create our PID file. server.setProcName(DHCP6_NAME); server.setConfigFile(config_file); server.createPIDFile(); try { - // Initialize the server, e.g. establish control session - // Read a configuration file + // Initialize the server. server.init(config_file); - } catch (const std::exception& ex) { try { @@ -245,8 +239,8 @@ main(int argc, char* argv[]) { LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what()); } catch (...) { // The exception thrown during the initialization could - // originate from logger subsystem. Therefore LOG_ERROR() may - // fail as well. + // originate from logger subsystem. Therefore LOG_ERROR() + // may fail as well. cerr << "Failed to initialize server: " << ex.what() << endl; } @@ -277,7 +271,6 @@ main(int argc, char* argv[]) { } ret = EXIT_FAILURE; } catch (const std::exception& ex) { - // First, we print the error on stderr (that should always work) cerr << DHCP6_NAME << "Fatal error during start up: " << ex.what() << endl; diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index 44890b1b2c..01cecaa41c 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -29,15 +29,14 @@ #include #include +#include #include #include +#include #include #include #include -#include - -#include using namespace std; using namespace isc; @@ -1096,6 +1095,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelStats) { "pkt6-receive-drop" }; + // preparing the schema which check if all statistics are set to zero std::ostringstream s; s << "{ \"arguments\": { "; for (auto st = initial_stats.begin(); st != initial_stats.end();) { diff --git a/src/lib/dhcp/docsis3_option_defs.h b/src/lib/dhcp/docsis3_option_defs.h index 879ffd56ec..fe66d04ebd 100644 --- a/src/lib/dhcp/docsis3_option_defs.h +++ b/src/lib/dhcp/docsis3_option_defs.h @@ -25,7 +25,8 @@ const OptionDefParams DOCSIS3_V4_DEFS[] = { }; /// Number of option definitions defined. -const int DOCSIS3_V4_DEFS_SIZE = sizeof(DOCSIS3_V4_DEFS) / sizeof(OptionDefParams); +const int DOCSIS3_V4_DEFS_SIZE = + sizeof(DOCSIS3_V4_DEFS) / sizeof(DOCSIS3_V4_DEFS[0]); /// @todo define remaining docsis3 v6 codes #define DOCSIS3_V6_ORO 1 @@ -65,7 +66,7 @@ const int DOCSIS3_V6_DEFS_SIZE = extern const char* DOCSIS3_CLASS_EROUTER; extern const char* DOCSIS3_CLASS_MODEM; -}; // isc::dhcp namespace -}; // isc namespace +} // namespace dhcp +} // namespace isc #endif // DOCSIS3_OPTION_DEFS_H diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index 6c37b77efd..b54b229daf 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -15,12 +15,11 @@ #include #include #include -#include #include #include #include #include -#include +#include #include #include @@ -28,35 +27,46 @@ #include #include +#include using namespace std; using namespace isc::dhcp; using namespace isc::util; +namespace isc { +namespace dhcp { + +namespace { + +const OptionDefParamsEncapsulation OPTION_DEF_PARAMS[] = { + { STANDARD_V4_OPTION_DEFINITIONS, STANDARD_V4_OPTION_DEFINITIONS_SIZE, DHCP4_OPTION_SPACE }, + { STANDARD_V6_OPTION_DEFINITIONS, STANDARD_V6_OPTION_DEFINITIONS_SIZE, DHCP6_OPTION_SPACE }, + { DOCSIS3_V4_DEFS, DOCSIS3_V4_DEFS_SIZE, DOCSIS3_V4_OPTION_SPACE }, + { DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE, DOCSIS3_V6_OPTION_SPACE }, + { ISC_V6_OPTION_DEFINITIONS, ISC_V6_OPTION_DEFINITIONS_SIZE, ISC_V6_OPTION_SPACE }, + { MAPE_V6_OPTION_DEFINITIONS, MAPE_V6_OPTION_DEFINITIONS_SIZE, MAPE_V6_OPTION_SPACE }, + { MAPT_V6_OPTION_DEFINITIONS, MAPT_V6_OPTION_DEFINITIONS_SIZE, MAPT_V6_OPTION_SPACE }, + { LW_V6_OPTION_DEFINITIONS, LW_V6_OPTION_DEFINITIONS_SIZE, LW_V6_OPTION_SPACE }, + { V4V6_RULE_OPTION_DEFINITIONS, V4V6_RULE_OPTION_DEFINITIONS_SIZE, V4V6_RULE_OPTION_SPACE }, + { V4V6_BIND_OPTION_DEFINITIONS, V4V6_BIND_OPTION_DEFINITIONS_SIZE, V4V6_BIND_OPTION_SPACE }, + { LAST_RESORT_V4_OPTION_DEFINITIONS, LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE, LAST_RESORT_V4_OPTION_SPACE }, + { NULL, 0, "" } +}; + +} // namespace + +} // namespace dhcp +} // namespace isc + // static array with factories for options std::map LibDHCP::v4factories_; // static array with factories for options std::map LibDHCP::v6factories_; -// Static container with DHCPv4 option definitions. -OptionDefContainerPtr LibDHCP::v4option_defs_(new OptionDefContainer()); - -// Static container with DHCPv6 option definitions. -OptionDefContainerPtr LibDHCP::v6option_defs_(new OptionDefContainer()); - // Static container with option definitions grouped by option space. OptionDefContainers LibDHCP::option_defs_; -// Static container with vendor option definitions for DHCPv4. -VendorOptionDefContainers LibDHCP::vendor4_defs_; - -// Static container with vendor option definitions for DHCPv6. -VendorOptionDefContainers LibDHCP::vendor6_defs_; - -// Static container with last resort option definitions for DHCPv4. -OptionDefContainerPtr LibDHCP::lastresort_defs_(new OptionDefContainer()); - // Static container with option definitions created in runtime. StagedValue LibDHCP::runtime_option_defs_; @@ -79,21 +89,12 @@ void initOptionSpace(OptionDefContainerPtr& defs, const OptionDefContainerPtr& LibDHCP::getOptionDefs(const std::string& space) { + static mutex local_mutex; + isc::util::thread::LockGuard lock(&local_mutex); // If any of the containers is not initialized, it means that we haven't // initialized option definitions at all. - if (v4option_defs_->empty()) { - initStdOptionDefs4(); - initVendorOptsDocsis4(); - initStdOptionDefs6(); - initVendorOptsDocsis6(); - initLastResortOptionDefs(); - } - - if (space == DHCP4_OPTION_SPACE) { - return (v4option_defs_); - - } else if (space == DHCP6_OPTION_SPACE) { - return (v6option_defs_); + if (option_defs_.end() == option_defs_.find(space)) { + initStdOptionDefs(space); } OptionDefContainers::const_iterator container = option_defs_.find(space); @@ -103,41 +104,20 @@ LibDHCP::getOptionDefs(const std::string& space) { return (null_option_def_container_); } -const OptionDefContainerPtr& -LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) { - - if (vendor_id == VENDOR_ID_CABLE_LABS && - vendor4_defs_.find(VENDOR_ID_CABLE_LABS) == vendor4_defs_.end()) { - initVendorOptsDocsis4(); - } - - VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id); - if (def == vendor4_defs_.end()) { - // No such vendor-id space - return (null_option_def_container_); - } - return (def->second); -} - -const OptionDefContainerPtr& -LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) { - - if (vendor_id == VENDOR_ID_CABLE_LABS && - vendor6_defs_.find(VENDOR_ID_CABLE_LABS) == vendor6_defs_.end()) { - initVendorOptsDocsis6(); - } - - if (vendor_id == ENTERPRISE_ID_ISC && - vendor6_defs_.find(ENTERPRISE_ID_ISC) == vendor6_defs_.end()) { - initVendorOptsIsc6(); - } - - VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id); - if (def == vendor6_defs_.end()) { - // No such vendor-id space - return (null_option_def_container_); +const OptionDefContainerPtr +LibDHCP::getVendorOptionDefs(const Option::Universe u, const uint32_t vendor_id) { + if (Option::V4 == u) { + if (VENDOR_ID_CABLE_LABS == vendor_id) { + return getOptionDefs(DOCSIS3_V4_OPTION_SPACE); + } + } else if (Option::V6 == u) { + if (VENDOR_ID_CABLE_LABS == vendor_id) { + return getOptionDefs(DOCSIS3_V6_OPTION_SPACE); + } else if (ENTERPRISE_ID_ISC == vendor_id) { + return getOptionDefs(ISC_V6_OPTION_SPACE); + } } - return (def->second); + return (null_option_def_container_); } OptionDefinitionPtr @@ -165,14 +145,13 @@ LibDHCP::getOptionDef(const std::string& space, const std::string& name) { OptionDefinitionPtr LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, const std::string& name) { - OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) : - getVendorOption6Defs(vendor_id)); + const OptionDefContainerPtr option_defs_ptr = getVendorOptionDefs(u, vendor_id); - if (!defs) { + if (!option_defs_ptr) { return (OptionDefinitionPtr()); } - const OptionDefContainerNameIndex& idx = defs->get<2>(); + const OptionDefContainerNameIndex& idx = option_defs_ptr->get<2>(); const OptionDefContainerNameRange& range = idx.equal_range(name); if (range.first != range.second) { return (*range.first); @@ -183,17 +162,16 @@ LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, OptionDefinitionPtr LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, const uint16_t code) { - OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) : - getVendorOption6Defs(vendor_id)); + const OptionDefContainerPtr option_defs_ptr = getVendorOptionDefs(u, vendor_id); - if (!defs) { + if (!option_defs_ptr) { // Weird universe or unknown vendor_id. We don't care. No definitions // one way or another // What is it anyway? return (OptionDefinitionPtr()); } - const OptionDefContainerTypeIndex& idx = defs->get<1>(); + const OptionDefContainerTypeIndex& idx = option_defs_ptr->get<1>(); const OptionDefContainerTypeRange& range = idx.equal_range(code); if (range.first != range.second) { return (*range.first); @@ -288,7 +266,7 @@ LibDHCP::getLastResortOptionDef(const std::string& space, const std::string& nam OptionDefContainerPtr LibDHCP::getLastResortOptionDefs(const std::string& space) { if (space == DHCP4_OPTION_SPACE) { - return (lastresort_defs_); + return getOptionDefs(LAST_RESORT_V4_OPTION_SPACE); } return (null_option_def_container_); } @@ -622,14 +600,15 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id, size_t length = buf.size(); // Get the list of option definitions for this particular vendor-id - const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption6Defs(vendor_id); + const OptionDefContainerPtr option_defs_ptr = + LibDHCP::getVendorOptionDefs(Option::V6, vendor_id); // Get the search index #1. It allows to search for option definitions // using option code. If there's no such vendor-id space, we're out of luck // anyway. const OptionDefContainerTypeIndex* idx = NULL; - if (option_defs) { - idx = &(option_defs->get<1>()); + if (option_defs_ptr) { + idx = &(option_defs_ptr->get<1>()); } // The buffer being read comprises a set of options, each starting with @@ -715,12 +694,13 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe size_t offset = 0; // Get the list of standard option definitions. - const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption4Defs(vendor_id); + const OptionDefContainerPtr option_defs_ptr = + LibDHCP::getVendorOptionDefs(Option::V4, vendor_id); // Get the search index #1. It allows to search for option definitions // using option code. const OptionDefContainerTypeIndex* idx = NULL; - if (option_defs) { - idx = &(option_defs->get<1>()); + if (option_defs_ptr) { + idx = &(option_defs_ptr->get<1>()); } // The buffer being read comprises a set of options, each starting with @@ -807,7 +787,7 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe options.insert(std::make_pair(opt_type, opt)); offset += opt_len; - } // end of data-chunk + } // end of data-chunk break; // end of the vendor block. } @@ -880,11 +860,10 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u, isc_throw(BadValue, "There is already DHCPv6 factory registered " << "for option type " << opt_type); } - v6factories_[opt_type]=factory; + v6factories_[opt_type] = factory; return; } - case Option::V4: - { + case Option::V4: { // Option 0 is special (a one octet-long, equal 0) PAD option. It is never // instantiated as an Option object, but rather consumed during packet parsing. if (opt_type == 0) { @@ -896,11 +875,11 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u, if (opt_type > 254) { isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed."); } - if (v4factories_.find(opt_type)!=v4factories_.end()) { + if (v4factories_.find(opt_type) != v4factories_.end()) { isc_throw(BadValue, "There is already DHCPv4 factory registered " << "for option type " << opt_type); } - v4factories_[opt_type]=factory; + v4factories_[opt_type] = factory; return; } default: @@ -910,50 +889,16 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u, return; } -void -LibDHCP::initStdOptionDefs4() { - initOptionSpace(v4option_defs_, STANDARD_V4_OPTION_DEFINITIONS, - STANDARD_V4_OPTION_DEFINITIONS_SIZE); -} - -void -LibDHCP::initStdOptionDefs6() { - initOptionSpace(v6option_defs_, STANDARD_V6_OPTION_DEFINITIONS, - STANDARD_V6_OPTION_DEFINITIONS_SIZE); - initOptionSpace(option_defs_[MAPE_V6_OPTION_SPACE], MAPE_V6_OPTION_DEFINITIONS, - MAPE_V6_OPTION_DEFINITIONS_SIZE); - initOptionSpace(option_defs_[MAPT_V6_OPTION_SPACE], MAPT_V6_OPTION_DEFINITIONS, - MAPT_V6_OPTION_DEFINITIONS_SIZE); - initOptionSpace(option_defs_[LW_V6_OPTION_SPACE], LW_V6_OPTION_DEFINITIONS, - LW_V6_OPTION_DEFINITIONS_SIZE); - initOptionSpace(option_defs_[V4V6_RULE_OPTION_SPACE], V4V6_RULE_OPTION_DEFINITIONS, - V4V6_RULE_OPTION_DEFINITIONS_SIZE); - initOptionSpace(option_defs_[V4V6_BIND_OPTION_SPACE], V4V6_BIND_OPTION_DEFINITIONS, - V4V6_BIND_OPTION_DEFINITIONS_SIZE); -} - -void -LibDHCP::initLastResortOptionDefs() { - initOptionSpace(lastresort_defs_, LAST_RESORT_V4_OPTION_DEFINITIONS, - LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE); -} - -void -LibDHCP::initVendorOptsDocsis4() { - initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS, - DOCSIS3_V4_DEFS_SIZE); -} - -void -LibDHCP::initVendorOptsDocsis6() { - initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, - DOCSIS3_V6_DEFS_SIZE); -} - -void -LibDHCP::initVendorOptsIsc6() { - initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_OPTION_DEFINITIONS, - ISC_V6_OPTION_DEFINITIONS_SIZE); +void LibDHCP::initStdOptionDefs(const std::string &space) { + if (option_defs_.end() == option_defs_.find(space)) { + option_defs_[space] = OptionDefContainerPtr(new OptionDefContainer); + for (int i = 0; OPTION_DEF_PARAMS[i].optionDefParams; i++) { + if (space == OPTION_DEF_PARAMS[i].space) { + initOptionSpace(option_defs_[space], OPTION_DEF_PARAMS[i].optionDefParams, OPTION_DEF_PARAMS[i].size); + break; + } + } + } } uint32_t @@ -969,7 +914,6 @@ LibDHCP::optionSpaceToVendorId(const std::string& option_space) { std::string x = option_space.substr(7); check = boost::lexical_cast(x); - } catch (const boost::bad_lexical_cast &) { return (0); } @@ -990,7 +934,6 @@ void initOptionSpace(OptionDefContainerPtr& defs, // case. if (!defs) { defs.reset(new OptionDefContainer()); - } else { defs->clear(); } diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h index 2d29f1474b..5182115ba0 100644 --- a/src/lib/dhcp/libdhcp++.h +++ b/src/lib/dhcp/libdhcp++.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -26,7 +27,7 @@ class LibDHCP { public: /// Map of factory functions. - typedef std::map FactoryMap; + typedef std::map FactoryMap; /// @brief Returns collection of option definitions. /// @@ -281,21 +282,15 @@ public: uint16_t type, Option::Factory * factory); - /// @brief Returns v4 option definitions for a given vendor + /// @brief Returns option definitions for given universe and vendor /// + /// @param u option universe /// @param vendor_id enterprise-id of a given vendor - /// @return a container for a given vendor (or NULL if no option - /// definitions are defined) - static const OptionDefContainerPtr& - getVendorOption4Defs(const uint32_t vendor_id); - - /// @brief Returns v6 option definitions for a given vendor /// - /// @param vendor_id enterprise-id of a given vendor /// @return a container for a given vendor (or NULL if no option /// definitions are defined) - static const OptionDefContainerPtr& - getVendorOption6Defs(const uint32_t vendor_id); + static const OptionDefContainerPtr + getVendorOptionDefs(Option::Universe u, const uint32_t vendor_id); /// @brief Parses provided buffer as DHCPv6 vendor options and creates /// Option objects. @@ -371,37 +366,14 @@ public: static uint32_t optionSpaceToVendorId(const std::string& option_space); private: - - /// Initialize standard DHCPv4 option definitions. + /// Initialize DHCP option definitions. /// - /// The method creates option definitions for all DHCPv4 options. - /// Currently this function is not implemented. + /// The method creates option definitions for all DHCP options. /// /// @throw std::bad alloc if system went out of memory. /// @throw MalformedOptionDefinition if any of the definitions /// are incorrect. This is programming error. - static void initStdOptionDefs4(); - - /// Initialize standard DHCPv6 option definitions. - /// - /// The method creates option definitions for all DHCPv6 options. - /// - /// @throw std::bad_alloc if system went out of memory. - /// @throw MalformedOptionDefinition if any of the definitions - /// is incorrect. This is a programming error. - static void initStdOptionDefs6(); - - /// Initialize last resort DHCPv4 option definitions. - static void initLastResortOptionDefs(); - - /// Initialize DOCSIS DHCPv4 option definitions. - static void initVendorOptsDocsis4(); - - /// Initialize DOCSIS DHCPv6 option definitions. - static void initVendorOptsDocsis6(); - - /// Initialize private DHCPv6 option definitions. - static void initVendorOptsIsc6(); + static void initStdOptionDefs(const std::string& space); /// pointers to factories that produce DHCPv6 options static FactoryMap v4factories_; @@ -409,24 +381,10 @@ private: /// pointers to factories that produce DHCPv6 options static FactoryMap v6factories_; - /// Container with DHCPv4 option definitions. - static OptionDefContainerPtr v4option_defs_; - - /// Container with DHCPv6 option definitions. - static OptionDefContainerPtr v6option_defs_; /// Container that holds option definitions for various option spaces. static OptionDefContainers option_defs_; - /// Container for v4 vendor option definitions - static VendorOptionDefContainers vendor4_defs_; - - /// Container for v6 vendor option definitions - static VendorOptionDefContainers vendor6_defs_; - - /// Container with DHCPv4 last resort option definitions. - static OptionDefContainerPtr lastresort_defs_; - /// Container for additional option definitions created in runtime. static util::StagedValue runtime_option_defs_; }; diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index 3e1fa740af..13b4f5c07f 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -708,9 +708,9 @@ OptionDefinition::writeToBuffer(Option::Universe u, OptionDataTypeUtil::writePrefix(PrefixLen(len), address, buf); return; - } + } case OPT_PSID_TYPE: - { + { std::string txt = value; // first let's remove any whitespaces diff --git a/src/lib/dhcp/option_space.h b/src/lib/dhcp/option_space.h index a19fb8910f..368184d4ee 100644 --- a/src/lib/dhcp/option_space.h +++ b/src/lib/dhcp/option_space.h @@ -13,13 +13,17 @@ #include #include -#define DHCP4_OPTION_SPACE "dhcp4" -#define DHCP6_OPTION_SPACE "dhcp6" -#define MAPE_V6_OPTION_SPACE "s46-cont-mape-options" -#define MAPT_V6_OPTION_SPACE "s46-cont-mapt-options" -#define LW_V6_OPTION_SPACE "s46-cont-lw-options" -#define V4V6_RULE_OPTION_SPACE "s46-rule-options" -#define V4V6_BIND_OPTION_SPACE "s46-v4v6bind-options" +#define DHCP4_OPTION_SPACE "dhcp4" +#define DHCP6_OPTION_SPACE "dhcp6" +#define DOCSIS3_V4_OPTION_SPACE "docsis3-v4" +#define DOCSIS3_V6_OPTION_SPACE "docsis3-v6" +#define ISC_V6_OPTION_SPACE "4o6" +#define MAPE_V6_OPTION_SPACE "s46-cont-mape-options" +#define MAPT_V6_OPTION_SPACE "s46-cont-mapt-options" +#define LW_V6_OPTION_SPACE "s46-cont-lw-options" +#define V4V6_RULE_OPTION_SPACE "s46-rule-options" +#define V4V6_BIND_OPTION_SPACE "s46-v4v6bind-options" +#define LAST_RESORT_V4_OPTION_SPACE "last-resort-v4" namespace isc { namespace dhcp { @@ -183,7 +187,7 @@ private: uint32_t enterprise_number_; ///< IANA assigned enterprise number. }; -} // namespace isc::dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // OPTION_SPACE_H diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index b1f1042446..495c5d7305 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -272,7 +272,8 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = { /// Number of option definitions defined. const int STANDARD_V4_OPTION_DEFINITIONS_SIZE = - sizeof(STANDARD_V4_OPTION_DEFINITIONS) / sizeof(STANDARD_V4_OPTION_DEFINITIONS[0]); + sizeof(STANDARD_V4_OPTION_DEFINITIONS) / + sizeof(STANDARD_V4_OPTION_DEFINITIONS[0]); /// Last resort definitions (only option 43 for now, these definitions /// are applied in deferred unpacking when none is found). @@ -281,7 +282,9 @@ const OptionDefParams LAST_RESORT_V4_OPTION_DEFINITIONS[] = { OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "vendor-encapsulated-options-space" } }; -const int LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE = 1; +const int LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE = + sizeof(LAST_RESORT_V4_OPTION_DEFINITIONS) / + sizeof(LAST_RESORT_V4_OPTION_DEFINITIONS[0]); /// Start Definition of DHCPv6 options @@ -425,7 +428,7 @@ const OptionDefParams STANDARD_V6_OPTION_DEFINITIONS[] = { { "v6-access-domain", D6O_V6_ACCESS_DOMAIN, OPT_FQDN_TYPE, false, NO_RECORD_DEF, "" }, { "sip-ua-cs-list", D6O_SIP_UA_CS_LIST, OPT_FQDN_TYPE, true, - NO_RECORD_DEF, "" }, + NO_RECORD_DEF, "" }, { "bootfile-url", D6O_BOOTFILE_URL, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" }, { "bootfile-param", D6O_BOOTFILE_PARAM, OPT_TUPLE_TYPE, true, NO_RECORD_DEF, "" }, { "client-arch-type", D6O_CLIENT_ARCH_TYPE, OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" }, @@ -544,9 +547,9 @@ const int V4V6_BIND_OPTION_DEFINITIONS_SIZE = sizeof(V4V6_BIND_OPTION_DEFINITIONS) / sizeof(V4V6_BIND_OPTION_DEFINITIONS[0]); -} // unnamed namespace +} // namespace -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // STD_OPTION_DEFS_H diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index 58f1656d16..ba68b02f91 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -437,7 +436,7 @@ TEST_F(LibDhcpTest, unpackOptions6) { EXPECT_NO_THROW ({ LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(v6packed)), - "dhcp6", options); + DHCP6_OPTION_SPACE, options); }); EXPECT_EQ(options.size(), 6); // there should be 5 options @@ -780,7 +779,7 @@ TEST_F(LibDhcpTest, unpackOptions4) { list deferred; ASSERT_NO_THROW( - LibDHCP::unpackOptions4(v4packed, "dhcp4", options, deferred); + LibDHCP::unpackOptions4(v4packed, DHCP4_OPTION_SPACE, options, deferred); ); isc::dhcp::OptionCollection::const_iterator x = options.find(12); @@ -1917,8 +1916,8 @@ TEST_F(LibDhcpTest, getOptionDefByName4) { // This test checks if the definition of the DHCPv6 vendor option can // be searched by option name. TEST_F(LibDhcpTest, getVendorOptionDefByName6) { - const OptionDefContainerPtr& defs = - LibDHCP::getVendorOption6Defs(VENDOR_ID_CABLE_LABS); + const OptionDefContainerPtr defs = + LibDHCP::getVendorOptionDefs(Option::V6, VENDOR_ID_CABLE_LABS); ASSERT_TRUE(defs); for (OptionDefContainer::const_iterator def = defs->begin(); def != defs->end(); ++def) { @@ -1933,8 +1932,8 @@ TEST_F(LibDhcpTest, getVendorOptionDefByName6) { // This test checks if the definition of the DHCPv4 vendor option can // be searched by option name. TEST_F(LibDhcpTest, getVendorOptionDefByName4) { - const OptionDefContainerPtr& defs = - LibDHCP::getVendorOption4Defs(VENDOR_ID_CABLE_LABS); + const OptionDefContainerPtr defs = + LibDHCP::getVendorOptionDefs(Option::V4, VENDOR_ID_CABLE_LABS); ASSERT_TRUE(defs); for (OptionDefContainer::const_iterator def = defs->begin(); def != defs->end(); ++def) { @@ -2079,7 +2078,7 @@ TEST_F(LibDhcpTest, vendorClass6) { isc::util::encode::decodeHex(vendor_class_hex, bin); ASSERT_NO_THROW ({ - LibDHCP::unpackOptions6(bin, "dhcp6", options); + LibDHCP::unpackOptions6(bin, DHCP6_OPTION_SPACE, options); }); EXPECT_EQ(options.size(), 1); // There should be 1 option. @@ -2198,7 +2197,7 @@ TEST_F(LibDhcpTest, sw46options) { OptionBuffer buf(mape_bin); - size_t parsed; + size_t parsed = 0; EXPECT_NO_THROW (parsed = LibDHCP::unpackOptions6(buf, "dhcp6", options)); EXPECT_EQ(mape_bin.size(), parsed); @@ -2257,4 +2256,4 @@ TEST_F(LibDhcpTest, sw46options) { EXPECT_EQ("type=00093, len=00004: 8 (uint8) len=6,psid=63 (psid)", portparam->toText()); } -} // end of anonymous space +} // end of anonymous space diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index d0076959dd..cf3d274fa5 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -118,6 +118,7 @@ libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h libkea_dhcpsrv_la_SOURCES += memfile_lease_storage.h libkea_dhcpsrv_la_SOURCES += multi_threading_utils.h multi_threading_utils.cc +libkea_dhcpsrv_la_SOURCES += thread_pool.cc thread_pool.h if HAVE_MYSQL libkea_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index d7a5551160..ad138d7460 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -33,21 +33,22 @@ #include #include -#include #include -#include +#include #include #include #include +#include + using namespace isc::asiolink; using namespace isc::dhcp; using namespace isc::dhcp_ddns; using namespace isc::hooks; using namespace isc::stats; +using namespace isc::util::thread; namespace { - /// Structure that holds registered hook indexes struct AllocEngineHooks { int hook_index_lease4_select_; ///< index for "lease4_receive" hook point @@ -80,13 +81,12 @@ struct AllocEngineHooks { // module is called. AllocEngineHooks Hooks; -}; // anonymous namespace +} // namespace namespace isc { namespace dhcp { - -AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type) - :Allocator(lease_type) { +AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type) : + Allocator(lease_type) { } isc::asiolink::IOAddress @@ -157,11 +157,10 @@ AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& } isc::asiolink::IOAddress -AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet, - const ClientClasses& client_classes, - const DuidPtr&, - const IOAddress&) { - +AllocEngine::IterativeAllocator::pickAddressInternal(const SubnetPtr& subnet, + const ClientClasses& client_classes, + const DuidPtr&, + const IOAddress&) { // Is this prefix allocation? bool prefix = pool_type_ == Lease::TYPE_PD; uint8_t prefix_len = 0; @@ -285,40 +284,35 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet, return (last); } -AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type) - :Allocator(lease_type) { +AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type) : + Allocator(lease_type) { isc_throw(NotImplemented, "Hashed allocator is not implemented"); } - isc::asiolink::IOAddress -AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&, - const ClientClasses&, - const DuidPtr&, - const IOAddress&) { +AllocEngine::HashedAllocator::pickAddressInternal(const SubnetPtr&, + const ClientClasses&, + const DuidPtr&, + const IOAddress&) { isc_throw(NotImplemented, "Hashed allocator is not implemented"); } -AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type) - :Allocator(lease_type) { +AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type) : + Allocator(lease_type) { isc_throw(NotImplemented, "Random allocator is not implemented"); } - isc::asiolink::IOAddress -AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&, - const ClientClasses&, - const DuidPtr&, - const IOAddress&) { +AllocEngine::RandomAllocator::pickAddressInternal(const SubnetPtr&, + const ClientClasses&, + const DuidPtr&, + const IOAddress&) { isc_throw(NotImplemented, "Random allocator is not implemented"); } - -AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts, - bool ipv6) - : attempts_(attempts), incomplete_v4_reclamations_(0), - incomplete_v6_reclamations_(0) { - +AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts, bool ipv6) : + attempts_(attempts), incomplete_v4_reclamations_(0), + incomplete_v6_reclamations_(0) { // Choose the basic (normal address) lease type Lease::Type basic_type = ipv6 ? Lease::TYPE_NA : Lease::TYPE_V4; @@ -377,7 +371,6 @@ AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) { } // end of namespace isc namespace { - /// @brief Checks if the specified address belongs to one of the subnets /// within a shared network. /// @@ -396,8 +389,8 @@ inAllowedPool(AllocEngine::ClientContext6& ctx, const Lease::Type& lease_type, // If the subnet belongs to a shared network we will be iterating // over the subnets that belong to this shared network. Subnet6Ptr current_subnet = ctx.subnet_; - while (current_subnet) { + while (current_subnet) { if (current_subnet->clientSupported(ctx.query_->getClasses())) { if (check_subnet) { if (current_subnet->inPool(lease_type, address)) { @@ -419,14 +412,12 @@ inAllowedPool(AllocEngine::ClientContext6& ctx, const Lease::Type& lease_type, } - // ########################################################################## // # DHCPv6 lease allocation code starts here. // ########################################################################## namespace isc { namespace dhcp { - AllocEngine::ClientContext6::ClientContext6() : query_(), fake_allocation_(false), subnet_(), host_subnet_(), duid_(), hwaddr_(), host_identifiers_(), hosts_(), fwd_dns_update_(false), @@ -447,7 +438,6 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname), callout_handle_(callout_handle), allocated_resources_(), new_leases_(), ias_(), ddns_params_() { - // Initialize host identifiers. if (duid) { addHostIdentifier(Host::IDENT_DUID, duid->getDuid()); @@ -611,7 +601,6 @@ void AllocEngine::findReservation(ClientContext6& ctx) { // We can only search for the reservation if a subnet has been selected. while (subnet) { - // Only makes sense to get reservations if the client has access // to the class and host reservations are enabled. if (subnet->clientSupported(ctx.query_->getClasses()) && @@ -623,7 +612,6 @@ void AllocEngine::findReservation(ClientContext6& ctx) { if (host_map.count(subnet->getID()) > 0) { ctx.hosts_[subnet->getID()] = host_map[subnet->getID()]; } - } else { // Attempt to find a host using a specified identifier. ConstHostPtr host = HostMgr::instance().get6(subnet->getID(), @@ -637,7 +625,6 @@ void AllocEngine::findReservation(ClientContext6& ctx) { } } } - } // We need to get to the next subnet if this is a shared network. If it @@ -664,10 +651,8 @@ AllocEngine::findGlobalReservation(ClientContext6& ctx) { return (host); } - Lease6Collection AllocEngine::allocateLeases6(ClientContext6& ctx) { - try { if (!ctx.subnet_) { isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation"); @@ -715,7 +700,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) { // Case 1: There are no leases and there's a reservation for this host. if (leases.empty() && !ctx.hosts_.empty()) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_ALLOC_NO_LEASES_HR) .arg(ctx.query_->getLabel()); @@ -739,7 +723,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) { // We will return these leases for the client, but we may need to update // FQDN information. } else if (!leases.empty() && ctx.hosts_.empty()) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_ALLOC_LEASES_NO_HR) .arg(ctx.query_->getLabel()); @@ -758,7 +741,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) { // Case 3: There are leases and there are reservations. } else if (!leases.empty() && !ctx.hosts_.empty()) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_ALLOC_LEASES_HR) .arg(ctx.query_->getLabel()); @@ -833,10 +815,7 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) { } return (leases); } - - } catch (const isc::Exception& e) { - // Some other error, return an empty lease. LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_ALLOC_ERROR) .arg(ctx.query_->getLabel()) @@ -848,7 +827,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) { Lease6Collection AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { - AllocatorPtr allocator = getAllocator(ctx.currentIA().type_); if (!allocator) { @@ -872,7 +850,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE; while (subnet) { - if (!subnet->clientSupported(ctx.query_->getClasses())) { subnet = subnet->getNextSubnet(original_subnet); continue; @@ -883,8 +860,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { // check if the hint is in pool and is available // This is equivalent of subnet->inPool(hint), but returns the pool pool = boost::dynamic_pointer_cast - (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(), - hint)); + (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(), hint)); // check if the pool is allowed if (pool && !pool->clientSupported(ctx.query_->getClasses())) { @@ -892,15 +868,14 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { } if (pool) { - // Check which host reservation mode is supported in this subnet. Network::HRMode hr_mode = subnet->getHostReservationMode(); /// @todo: We support only one hint for now Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, hint); - if (!lease) { + if (!lease) { // In-pool reservations: Check if this address is reserved for someone // else. There is no need to check for whom it is reserved, because if // it has been reserved for us we would have already allocated a lease. @@ -923,7 +898,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { // no longer usable and we need to continue the regular // allocation path. if (lease) { - /// @todo: We support only one lease per ia for now Lease6Collection collection; collection.push_back(lease); @@ -935,12 +909,9 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { .arg(ctx.query_->getLabel()) .arg(hint.toText()); } - } else { - // If the lease is expired, we may likely reuse it, but... if (lease->expired()) { - ConstHostPtr host; if (hr_mode != Network::HR_DISABLED) { host = HostMgr::instance().get6(subnet->getID(), hint); @@ -948,7 +919,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { // Let's check if there is a reservation for this address. if (!host) { - // Copy an existing, expired lease so as it can be returned // to the caller. Lease6Ptr old_lease(new Lease6(*lease)); @@ -961,7 +931,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { /// @todo: We support only one lease per ia for now leases.push_back(lease); return (leases); - } else { LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_EXPIRED_HINT_RESERVED) @@ -1000,7 +969,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { ctx.subnet_ = subnet = original_subnet; while (subnet) { - if (!subnet->clientSupported(ctx.query_->getClasses())) { subnet = subnet->getNextSubnet(original_subnet); continue; @@ -1031,7 +999,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { } for (uint64_t i = 0; i < max_attempts; ++i) { - ++total_attempts; IOAddress candidate = allocator->pickAddress(subnet, @@ -1044,7 +1011,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { /// it has been reserved for us we would have already allocated a lease. if (hr_mode == Network::HR_ALL && HostMgr::instance().get6(subnet->getID(), candidate)) { - // Don't allocate. continue; } @@ -1063,12 +1029,11 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { } Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, - candidate); - if (!existing) { + candidate); + if (!existing) { // there's no existing lease for selected candidate, so it is // free. Let's allocate it. - ctx.subnet_ = subnet; Lease6Ptr lease = createLease6(ctx, candidate, prefix_len, callout_status); if (lease) { @@ -1078,7 +1043,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { leases.push_back(lease); return (leases); - } else if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) { // Don't retry when the callout status is not continue. @@ -1120,7 +1084,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { void AllocEngine::allocateReservedLeases6(ClientContext6& ctx, Lease6Collection& existing_leases) { - // If there are no reservations or the reservation is v4, there's nothing to do. if (ctx.hosts_.empty()) { LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, @@ -1201,7 +1164,6 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx, Subnet6Ptr subnet = ctx.subnet_; while (subnet) { - SubnetID subnet_id = subnet->getID(); // No hosts for this subnet or the subnet not supported. @@ -1228,9 +1190,7 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx, // If there's a lease for this address, let's not create it. // It doesn't matter whether it is for this client or for someone else. - if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, - addr)) { - + if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) { // Let's remember the subnet from which the reserved address has been // allocated. We'll use this subnet for allocating other reserved // resources. @@ -1264,7 +1224,6 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx, // ... and add it to the existing leases list. existing_leases.push_back(lease); - if (ctx.currentIA().type_ == Lease::TYPE_NA) { LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_HR_ADDR_GRANTED) .arg(addr.toText()) @@ -1287,7 +1246,6 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx, // would work for any number of reservations. return; } - } subnet = subnet->getNextSubnet(ctx.subnet_); @@ -1364,7 +1322,6 @@ AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx, // If there's a lease for this address, let's not create it. // It doesn't matter whether it is for this client or for someone else. if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) { - if (!ghost->getHostname().empty()) { // If there is a hostname reservation here we should stick // to this reservation. By updating the hostname in the @@ -1545,7 +1502,6 @@ AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx, bool AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) { - bool removed = false; for (Lease6Collection::iterator lease = container.begin(); lease != container.end(); ++lease) { @@ -1576,7 +1532,6 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx, // leases for deletion, by setting appropriate pointers to NULL. for (Lease6Collection::iterator lease = existing_leases.begin(); lease != existing_leases.end(); ++lease) { - // If there is reservation for this keep it. IPv6Resrv resv = makeIPv6Resrv(*(*lease)); if (ctx.hasGlobalReservation(resv) || @@ -1616,7 +1571,6 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx, // If there's only one lease left, break the loop. break; } - } // Remove all elements that we previously marked for deletion (those that @@ -1629,7 +1583,6 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx, uint8_t prefix_len, CalloutHandle::CalloutNextStep& callout_status) { - if (!expired->expired()) { isc_throw(BadValue, "Attempt to recycle lease that is still valid"); } @@ -1756,7 +1709,6 @@ Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx, const IOAddress& addr, uint8_t prefix_len, CalloutHandle::CalloutNextStep& callout_status) { - if (ctx.currentIA().type_ != Lease::TYPE_PD) { prefix_len = 128; // non-PD lease types must be always /128 } @@ -1883,12 +1835,12 @@ AllocEngine::renewLeases6(ClientContext6& ctx) { *ctx.duid_, ctx.currentIA().iaid_, subnet->getID()); + leases.insert(leases.end(), leases_subnet.begin(), leases_subnet.end()); subnet = subnet->getNextSubnet(ctx.subnet_); } - if (!leases.empty()) { LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_RENEW_REMOVE_RESERVED) @@ -1900,7 +1852,6 @@ AllocEngine::renewLeases6(ClientContext6& ctx) { } if (!ctx.hosts_.empty()) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_RENEW_HR) .arg(ctx.query_->getLabel()); @@ -1920,7 +1871,6 @@ AllocEngine::renewLeases6(ClientContext6& ctx) { // new leases during renewals. This is controlled with the // allow_new_leases_in_renewals_ field. if (leases.empty()) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_EXTEND_ALLOC_UNRESERVED) .arg(ctx.query_->getLabel()); @@ -1949,9 +1899,7 @@ AllocEngine::renewLeases6(ClientContext6& ctx) { } return (leases); - } catch (const isc::Exception& e) { - // Some other error, return an empty lease. LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_EXTEND_ERROR) .arg(ctx.query_->getLabel()) @@ -1963,7 +1911,6 @@ AllocEngine::renewLeases6(ClientContext6& ctx) { void AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) { - if (!lease || !ctx.subnet_) { return; } @@ -2108,7 +2055,6 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) { // Now that the lease has been reclaimed, we can go ahead and update it // in the lease database. LeaseMgrFactory::instance().updateLease6(lease); - } else { // Copy back the original date to the lease. For MySQL it doesn't make // much sense, but for memfile, the Lease6Ptr points to the actual lease @@ -2122,7 +2068,6 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) { ctx.currentIA().changed_leases_.push_back(old_data); } - Lease6Collection AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) { Lease6Collection updated_leases; @@ -2132,8 +2077,8 @@ AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases lease->fqdn_fwd_ = ctx.fwd_dns_update_; lease->fqdn_rev_ = ctx.rev_dns_update_; lease->hostname_ = ctx.hostname_; - if (!ctx.fake_allocation_) { + if (!ctx.fake_allocation_) { if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED) { // Transition lease state to default (aka assigned) lease->state_ = Lease::STATE_DEFAULT; @@ -2169,7 +2114,6 @@ void AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout, const bool remove_lease, const uint16_t max_unwarned_cycles) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_LEASES_RECLAMATION_START) .arg(max_leases) @@ -2199,7 +2143,6 @@ AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeo leases.pop_back(); incomplete_reclamation = true; } - } else { // If there is no limitation on the number of leases to reclaim, // we will try to process all. Hence, we don't mark it as incomplete @@ -2217,12 +2160,10 @@ AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeo size_t leases_processed = 0; BOOST_FOREACH(Lease6Ptr lease, leases) { - try { // Reclaim the lease. reclaimExpiredLease(lease, remove_lease, callout_handle); ++leases_processed; - } catch (const std::exception& ex) { LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED) .arg(lease->addr_.toText()) @@ -2271,7 +2212,6 @@ AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeo // We issued a warning, so let's now reset the counter. incomplete_v6_reclamations_ = 0; } - } else { // This was a complete reclamation, so let's reset the counter. incomplete_v6_reclamations_ = 0; @@ -2292,7 +2232,6 @@ AllocEngine::deleteExpiredReclaimedLeases6(const uint32_t secs) { // Try to delete leases from the lease database. LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs); - } catch (const std::exception& ex) { LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED) .arg(ex.what()); @@ -2303,12 +2242,10 @@ AllocEngine::deleteExpiredReclaimedLeases6(const uint32_t secs) { .arg(deleted_leases); } - void AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout, const bool remove_lease, const uint16_t max_unwarned_cycles) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_LEASES_RECLAMATION_START) .arg(max_leases) @@ -2338,7 +2275,6 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo leases.pop_back(); incomplete_reclamation = true; } - } else { // If there is no limitation on the number of leases to reclaim, // we will try to process all. Hence, we don't mark it as incomplete @@ -2346,7 +2282,6 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo lease_mgr.getExpiredLeases4(leases, max_leases); } - // Do not initialize the callout handle until we know if there are any // lease4_expire callouts installed. CalloutHandlePtr callout_handle; @@ -2357,12 +2292,10 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo size_t leases_processed = 0; BOOST_FOREACH(Lease4Ptr lease, leases) { - try { // Reclaim the lease. reclaimExpiredLease(lease, remove_lease, callout_handle); ++leases_processed; - } catch (const std::exception& ex) { LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED) .arg(lease->addr_.toText()) @@ -2411,7 +2344,6 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo // We issued a warning, so let's now reset the counter. incomplete_v4_reclamations_ = 0; } - } else { // This was a complete reclamation, so let's reset the counter. incomplete_v4_reclamations_ = 0; @@ -2445,7 +2377,6 @@ void AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease, const DbReclaimMode& reclaim_mode, const CalloutHandlePtr& callout_handle) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_LEASE_RECLAIM) .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_)) @@ -2457,8 +2388,8 @@ AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease, // it reclaims the lease itself. In this case the reclamation routine // will not update DNS nor update the database. bool skipped = false; - if (callout_handle) { + if (callout_handle) { // Use the RAII wrapper to make sure that the callout handle state is // reset when this object goes out of scope. All hook points must do // it to prevent possible circular dependency between the callout @@ -2479,7 +2410,6 @@ AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease, /// Not sure if we need to support every possible status everywhere. if (!skipped) { - // Generate removal name change request for D2, if required. // This will return immediately if the DNS wasn't updated // when the lease was created. @@ -2519,14 +2449,12 @@ AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease, lease->subnet_id_, "assigned-nas"), int64_t(-1)); - } else if (lease->type_ == Lease::TYPE_PD) { // IA_PD StatsMgr::instance().addValue(StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"), int64_t(-1)); - } // Increase total number of reclaimed leases. @@ -2543,7 +2471,6 @@ void AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease, const DbReclaimMode& reclaim_mode, const CalloutHandlePtr& callout_handle) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_LEASE_RECLAIM) .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_)) @@ -2554,8 +2481,8 @@ AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease, // it reclaims the lease itself. In this case the reclamation routine // will not update DNS nor update the database. bool skipped = false; - if (callout_handle) { + if (callout_handle) { // Use the RAII wrapper to make sure that the callout handle state is // reset when this object goes out of scope. All hook points must do // it to prevent possible circular dependency between the callout @@ -2575,7 +2502,6 @@ AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease, /// Not sure if we need to support every possible status everywhere. if (!skipped) { - // Generate removal name change request for D2, if required. // This will return immediately if the DNS wasn't updated // when the lease was created. @@ -2635,7 +2561,6 @@ AllocEngine::deleteExpiredReclaimedLeases4(const uint32_t secs) { // Try to delete leases from the lease database. LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs); - } catch (const std::exception& ex) { LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED) .arg(ex.what()); @@ -2648,13 +2573,11 @@ AllocEngine::deleteExpiredReclaimedLeases4(const uint32_t secs) { bool AllocEngine::reclaimDeclined(const Lease4Ptr& lease) { - if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) { return (true); } if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_recover_)) { - // Let's use a static callout handle. It will be initialized the first // time lease4_recover is called and will keep to that value. static CalloutHandlePtr callout_handle; @@ -2709,13 +2632,11 @@ AllocEngine::reclaimDeclined(const Lease4Ptr& lease) { bool AllocEngine::reclaimDeclined(const Lease6Ptr& lease) { - if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) { return (true); } if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease6_recover_)) { - // Let's use a static callout handle. It will be initialized the first // time lease6_recover is called and will keep to that value. static CalloutHandlePtr callout_handle; @@ -2769,20 +2690,17 @@ AllocEngine::reclaimDeclined(const Lease6Ptr& lease) { return (true); } - template void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease, const bool remove_lease, const boost::function& lease_update_fun) const { - LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); // Reclaim the lease - depending on the configuration, set the // expired-reclaimed state or simply remove it. if (remove_lease) { lease_mgr.deleteLease(lease->addr_); - } else if (!lease_update_fun.empty()) { // Clear FQDN information as we have already sent the // name change request to remove the DNS record. @@ -2791,7 +2709,6 @@ void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease, lease->fqdn_rev_ = false; lease->state_ = Lease::STATE_EXPIRED_RECLAIMED; lease_update_fun(lease); - } else { return; } @@ -2802,16 +2719,14 @@ void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease, .arg(lease->addr_.toText()); } - -} // end of isc::dhcp namespace -} // end of isc namespace +} // namespace dhcp +} // namespace isc // ########################################################################## // # DHCPv4 lease allocation code starts here. // ########################################################################## namespace { - /// @brief Check if the specific address is reserved for another client. /// /// This function finds a host reservation for a given address and then @@ -2932,7 +2847,6 @@ void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) for (Subnet4Ptr subnet = original_subnet; subnet; subnet = subnet->getNextSubnet(original_subnet, ctx.query_->getClasses())) { - // If client identifier has been supplied and the server wasn't // explicitly configured to ignore client identifiers for this subnet // check if there is a lease within this subnet. @@ -2952,7 +2866,6 @@ void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) // If no lease found using the client identifier, try the lookup using // the HW address. if (!client_lease && ctx.hwaddr_) { - // Get all leases for this HW address. Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_); @@ -3000,8 +2913,8 @@ inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) { // If the subnet belongs to a shared network we will be iterating // over the subnets that belong to this shared network. Subnet4Ptr current_subnet = ctx.subnet_; - while (current_subnet) { + while (current_subnet) { if (current_subnet->inPool(Lease::TYPE_V4, address, ctx.query_->getClasses())) { // We found a subnet that this address belongs to, so it @@ -3018,11 +2931,10 @@ inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) { return (false); } -} // end of anonymous namespace +} // namespace namespace isc { namespace dhcp { - AllocEngine::ClientContext4::ClientContext4() : subnet_(), clientid_(), hwaddr_(), requested_address_(IOAddress::IPV4_ZERO_ADDRESS()), @@ -3048,7 +2960,6 @@ AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet, fake_allocation_(fake_allocation), old_lease_(), new_lease_(), hosts_(), host_identifiers_(), ddns_params_(new DdnsParams()) { - // Initialize host identifiers. if (hwaddr) { addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_); @@ -3117,11 +3028,9 @@ AllocEngine::allocateLease4(ClientContext4& ctx) { if (ctx.fake_allocation_) { return (discoverLease4(ctx)); - } else { ctx.new_lease_ = requestLease4(ctx); } - } catch (const isc::Exception& e) { // Some other error, return an empty lease. LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_ALLOC_ERROR) @@ -3189,7 +3098,6 @@ AllocEngine::findReservation(ClientContext4& ctx) { // We can only search for the reservation if a subnet has been selected. while (subnet) { - // Only makes sense to get reservations if the client has access // to the class. if (subnet->clientSupported(ctx.query_->getClasses()) && @@ -3202,7 +3110,6 @@ AllocEngine::findReservation(ClientContext4& ctx) { ctx.hosts_[subnet->getID()] = host_map[subnet->getID()]; break; } - } else { // Attempt to find a host using a specified identifier. ConstHostPtr host = HostMgr::instance().get4(subnet->getID(), @@ -3242,7 +3149,6 @@ AllocEngine::findGlobalReservation(ClientContext4& ctx) { return (host); } - Lease4Ptr AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) { // Find an existing lease for this client. This function will return true @@ -3260,7 +3166,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) { // Check if there is a reservation for the client. If there is, we want to // assign the reserved address, rather than any other one. if (hasAddressReservation(ctx)) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_DISCOVER_HR) .arg(ctx.query_->getLabel()) @@ -3286,7 +3191,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) { .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() : "(no lease info)"); } - } else { new_lease = renewLease4(client_lease, ctx); } @@ -3303,7 +3207,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) { // which the reservation has just been removed. if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) && !addressReserved(client_lease->addr_, ctx)) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_OFFER_EXISTING_LEASE) .arg(ctx.query_->getLabel()); @@ -3321,7 +3224,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) { if (!new_lease && !ctx.requested_address_.isV4Zero() && inAllowedPool(ctx, ctx.requested_address_) && !addressReserved(ctx.requested_address_, ctx)) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_OFFER_REQUESTED_LEASE) .arg(ctx.requested_address_.toText()) @@ -3335,7 +3237,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) { // addresses. We will now use the allocator to pick the address // from the dynamic pool. if (!new_lease) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_OFFER_NEW_LEASE) .arg(ctx.query_->getLabel()); @@ -3362,9 +3263,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { Lease4Ptr client_lease; findClientLease(ctx, client_lease); - // Obtain the sole instance of the LeaseMgr. - LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); - // When the client sends the DHCPREQUEST, it should always specify the // address which it is requesting or renewing. That is, the client should // either use the requested IP address option or set the ciaddr. However, @@ -3376,7 +3274,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { // is not reserved for another client. If it is, stop here because // we can't allocate this address. if (addressReserved(ctx.requested_address_, ctx)) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_REQUEST_ADDRESS_RESERVED) .arg(ctx.query_->getLabel()) @@ -3384,7 +3281,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { return (Lease4Ptr()); } - } else if (hasAddressReservation(ctx)) { // The client hasn't specified an address to allocate, so the // allocation engine needs to find an appropriate address. @@ -3409,7 +3305,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { if (existing && !existing->expired() && !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ? ctx.clientid_ : ClientIdPtr())) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_REQUEST_IN_USE) .arg(ctx.query_->getLabel()) @@ -3431,7 +3326,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { // address, return NULL. The client should go back to the // DHCPDISCOVER and the reserved address will be offered. if (!existing || existing->expired()) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_REQUEST_INVALID) .arg(ctx.query_->getLabel()) @@ -3448,7 +3342,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { if ((!hasAddressReservation(ctx) || (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) && !inAllowedPool(ctx, ctx.requested_address_)) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_REQUEST_OUT_OF_POOL) .arg(ctx.query_->getLabel()) @@ -3471,7 +3364,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { ctx.requested_address_.isV4Zero()) && (hasAddressReservation(ctx) || inAllowedPool(ctx, client_lease->addr_))) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_REQUEST_EXTEND_LEASE) .arg(ctx.query_->getLabel()) @@ -3488,7 +3380,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { // The client doesn't have the lease or it is requesting an address // which it doesn't have. Let's try to allocate the requested address. if (!ctx.requested_address_.isV4Zero()) { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_REQUEST_ALLOC_REQUESTED) .arg(ctx.query_->getLabel()) @@ -3502,9 +3393,7 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE; new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx, callout_status); - } else { - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V4_REQUEST_PICK_ADDRESS) .arg(ctx.query_->getLabel()); @@ -3526,7 +3415,7 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) { .arg(ctx.query_->getLabel()) .arg(client_lease->addr_.toText()); - lease_mgr.deleteLease(client_lease->addr_); + LeaseMgrFactory::instance().deleteLease(client_lease->addr_); // Need to decrease statistic for assigned addresses. StatsMgr::instance().addValue( @@ -3580,7 +3469,6 @@ AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr, // Let's execute all callouts registered for lease4_select if (ctx.callout_handle_ && HooksManager::getHooksManager().calloutsPresent(hook_index_lease4_select_)) { - // Use the RAII wrapper to make sure that the callout handle state is // reset when this object goes out of scope. All hook points must do // it to prevent possible circular dependency between the callout @@ -3628,8 +3516,8 @@ AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr, if (!ctx.fake_allocation_) { // That is a real (REQUEST) allocation bool status = LeaseMgrFactory::instance().addLease(lease); - if (status) { + if (status) { // The lease insertion succeeded, let's bump up the statistic. StatsMgr::instance().addValue( StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-addresses"), @@ -3680,7 +3568,6 @@ AllocEngine::renewLease4(const Lease4Ptr& lease, // involves execution of hooks and DNS update. if (ctx.old_lease_->expired()) { reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_); - } lease->state_ = Lease::STATE_DEFAULT; @@ -3690,7 +3577,6 @@ AllocEngine::renewLease4(const Lease4Ptr& lease, // Execute all callouts registered for lease4_renew. if (HooksManager::getHooksManager(). calloutsPresent(Hooks.hook_index_lease4_renew_)) { - // Use the RAII wrapper to make sure that the callout handle state is // reset when this object goes out of scope. All hook points must do // it to prevent possible circular dependency between the callout @@ -3747,6 +3633,7 @@ AllocEngine::renewLease4(const Lease4Ptr& lease, static_cast(1)); } } + if (skip) { // Rollback changes (really useful only for memfile) /// @todo: remove this? @@ -3786,7 +3673,6 @@ AllocEngine::reuseExpiredLease4(Lease4Ptr& expired, // Let's execute all callouts registered for lease4_select if (ctx.callout_handle_ && HooksManager::getHooksManager() .calloutsPresent(hook_index_lease4_select_)) { - // Enable copying options from the packet within hook library. ScopedEnableOptionsCopy query4_options_copy(ctx.query_); @@ -3863,14 +3749,12 @@ AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& c if (exist_lease->expired()) { ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease)); return (reuseExpiredLease4(exist_lease, ctx, callout_status)); - } else { // If there is a lease and it is not expired, pass this lease back // to the caller in the context. The caller may need to know // which lease we're conflicting with. ctx.conflicting_lease_ = exist_lease; } - } else { return (createLease4(ctx, candidate, callout_status)); } @@ -3906,8 +3790,8 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) { Subnet4Ptr original_subnet = subnet; uint64_t total_attempts = 0; - while (subnet) { + while (subnet) { ClientIdPtr client_id; if (subnet->getMatchClientId()) { client_id = ctx.clientid_; @@ -3931,14 +3815,12 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) { ctx.requested_address_); // If address is not reserved for another client, try to allocate it. if (!addressReserved(candidate, ctx)) { - // The call below will return the non-NULL pointer if we // successfully allocate this lease. This means that the // address is not in use by another client. new_lease = allocateOrReuseLease4(candidate, ctx, callout_status); if (new_lease) { return (new_lease); - } else if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) { // Don't retry when the callout status is not continue. @@ -4003,5 +3885,5 @@ AllocEngine::conditionalExtendLifetime(Lease& lease) const { return (true); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h index ce5649dd8c..5ea6c073c9 100644 --- a/src/lib/dhcpsrv/alloc_engine.h +++ b/src/lib/dhcpsrv/alloc_engine.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ #include #include +#include #include #include @@ -44,8 +46,9 @@ public: /// @param file name of the file, where exception occurred /// @param line line of the file, where exception occurred /// @param what text description of the issue that caused exception - AllocFailed(const char* file, size_t line, const char* what) - : isc::Exception(file, line, what) {} + AllocFailed(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { + } }; /// @brief DHCPv4 and DHCPv6 allocation engine @@ -58,14 +61,12 @@ public: /// @todo: Does not handle out of allocation attempts well class AllocEngine : public boost::noncopyable { protected: - /// @brief base class for all address/prefix allocation algorithms /// /// This is an abstract class that should not be used directly, but rather /// specialized implementations should be used instead. class Allocator { public: - /// @brief picks one address out of available pools in a given subnet /// /// This method returns one address from the available pools in the @@ -94,23 +95,35 @@ protected: pickAddress(const SubnetPtr& subnet, const ClientClasses& client_classes, const DuidPtr& duid, - const isc::asiolink::IOAddress& hint) = 0; + const isc::asiolink::IOAddress& hint) { + isc::util::thread::LockGuard lock(&mutex_); + return pickAddressInternal(subnet, client_classes, duid, hint); + } /// @brief Default constructor. /// /// Specifies which type of leases this allocator will assign /// @param pool_type specifies pool type (addresses, temp. addr or prefixes) - Allocator(Lease::Type pool_type) - :pool_type_(pool_type) { + Allocator(Lease::Type pool_type) : pool_type_(pool_type) { } /// @brief virtual destructor virtual ~Allocator() { } - protected: + private: + virtual isc::asiolink::IOAddress + pickAddressInternal(const SubnetPtr& subnet, + const ClientClasses& client_classes, + const DuidPtr& duid, + const isc::asiolink::IOAddress& hint) = 0; + + protected: /// @brief defines pool type allocation Lease::Type pool_type_; + + private: + std::mutex mutex_; }; /// defines a pointer to allocator @@ -124,13 +137,13 @@ protected: /// over). class IterativeAllocator : public Allocator { public: - /// @brief default constructor /// /// Does not do anything /// @param type - specifies allocation type IterativeAllocator(Lease::Type type); + private: /// @brief returns the next address from pools in a subnet /// /// @param subnet next address will be returned from pool of that subnet @@ -139,12 +152,12 @@ protected: /// @param hint client's hint (ignored) /// @return the next address virtual isc::asiolink::IOAddress - pickAddress(const SubnetPtr& subnet, - const ClientClasses& client_classes, - const DuidPtr& duid, - const isc::asiolink::IOAddress& hint); - protected: + pickAddressInternal(const SubnetPtr& subnet, + const ClientClasses& client_classes, + const DuidPtr& duid, + const isc::asiolink::IOAddress& hint); + protected: /// @brief Returns the next prefix /// /// This method works for IPv6 addresses only. It increases the @@ -171,7 +184,6 @@ protected: static isc::asiolink::IOAddress increaseAddress(const isc::asiolink::IOAddress& address, bool prefix, const uint8_t prefix_len); - }; /// @brief Address/prefix allocator that gets an address based on a hash @@ -179,11 +191,11 @@ protected: /// @todo: This is a skeleton class for now and is missing an implementation. class HashedAllocator : public Allocator { public: - /// @brief default constructor (does nothing) /// @param type - specifies allocation type HashedAllocator(Lease::Type type); + private: /// @brief returns an address based on hash calculated from client's DUID. /// /// @todo: Implement this method @@ -194,10 +206,10 @@ protected: /// @param hint a hint (last address that was picked) /// @return selected address virtual isc::asiolink::IOAddress - pickAddress(const SubnetPtr& subnet, - const ClientClasses& client_classes, - const DuidPtr& duid, - const isc::asiolink::IOAddress& hint); + pickAddressInternal(const SubnetPtr& subnet, + const ClientClasses& client_classes, + const DuidPtr& duid, + const isc::asiolink::IOAddress& hint); }; /// @brief Random allocator that picks address randomly @@ -205,11 +217,11 @@ protected: /// @todo: This is a skeleton class for now and is missing an implementation. class RandomAllocator : public Allocator { public: - /// @brief default constructor (does nothing) /// @param type - specifies allocation type RandomAllocator(Lease::Type type); + private: /// @brief returns a random address from pool of specified subnet /// /// @todo: Implement this method @@ -220,14 +232,13 @@ protected: /// @param hint the last address that was picked (ignored) /// @return a random address from the pool virtual isc::asiolink::IOAddress - pickAddress(const SubnetPtr& subnet, - const ClientClasses& client_classes, - const DuidPtr& duid, - const isc::asiolink::IOAddress& hint); + pickAddressInternal(const SubnetPtr& subnet, + const ClientClasses& client_classes, + const DuidPtr& duid, + const isc::asiolink::IOAddress& hint); }; public: - /// @brief specifies allocation type typedef enum { ALLOC_ITERATIVE, // iterative - one address after another @@ -259,7 +270,6 @@ public: AllocatorPtr getAllocator(Lease::Type type); private: - /// @brief a pointer to currently used allocator /// /// For IPv4, there will be only one allocator: TYPE_V4 @@ -274,7 +284,6 @@ private: int hook_index_lease6_select_; ///< index for lease6_select hook public: - /// @brief Defines a single hint /// /// This is an entry that represents what the client had requested, @@ -285,7 +294,6 @@ public: /// @note Seems to be used only for DHCPv6. class Resource { public: - /// @brief Default constructor. /// /// @param address the address or prefix @@ -348,7 +356,6 @@ public: } protected: - /// @brief The address or prefix. isc::asiolink::IOAddress address_; @@ -416,7 +423,6 @@ public: /// information to the allocation engine methods is that adding /// new information doesn't modify the API of the allocation engine. struct ClientContext6 : public boost::noncopyable { - /// @name Parameters pertaining to DHCPv6 message //@{ @@ -481,7 +487,6 @@ public: /// @brief A collection of newly allocated leases. Lease6Collection new_leases_; - //@} /// @brief Parameters pertaining to individual IAs. @@ -924,7 +929,6 @@ public: } private: - /// @brief creates a lease and inserts it in LeaseMgr if necessary /// /// Creates a lease based on specified parameters and tries to insert it @@ -1249,7 +1253,6 @@ private: bool reclaimDeclined(const Lease6Ptr& lease); public: - /// @brief Context information for the DHCPv4 lease allocation. /// /// This structure holds a set of information provided by the DHCPv4 @@ -1521,7 +1524,6 @@ public: static ConstHostPtr findGlobalReservation(ClientContext4& ctx); private: - /// @brief Offers the lease. /// /// This method is called by the @c AllocEngine::allocateLease4 when @@ -1744,7 +1746,6 @@ private: bool conditionalExtendLifetime(Lease& lease) const; private: - /// @brief Number of consecutive DHCPv4 leases' reclamations after /// which there are still expired leases in the database. uint16_t incomplete_v4_reclamations_; @@ -1757,7 +1758,7 @@ private: /// @brief A pointer to the @c AllocEngine object. typedef boost::shared_ptr AllocEnginePtr; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif // ALLOC_ENGINE_H diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index 213ef0263f..9d93ad20d9 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -39,6 +39,8 @@ SrvConfig::SrvConfig() cfg_host_operations6_(CfgHostOperations::createConfig6()), class_dictionary_(new ClientClassDictionary()), decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0), + server_threads_(0), + server_max_thread_queue_size_(0), d2_client_config_(new D2ClientConfig()), configured_globals_(Element::createMap()), cfg_consist_(new CfgConsistency()) { @@ -57,6 +59,8 @@ SrvConfig::SrvConfig(const uint32_t sequence) cfg_host_operations6_(CfgHostOperations::createConfig6()), class_dictionary_(new ClientClassDictionary()), decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0), + server_threads_(0), + server_max_thread_queue_size_(0), d2_client_config_(new D2ClientConfig()), configured_globals_(Element::createMap()), cfg_consist_(new CfgConsistency()) { @@ -251,7 +255,6 @@ SrvConfig::mergeGlobals(SrvConfig& other) { void SrvConfig::removeStatistics() { - // Removes statistics for v4 and v6 subnets getCfgSubnets4()->removeStatistics(); diff --git a/src/lib/dhcpsrv/srv_config.h b/src/lib/dhcpsrv/srv_config.h index 4d4dca7297..7e26c7f0d0 100644 --- a/src/lib/dhcpsrv/srv_config.h +++ b/src/lib/dhcpsrv/srv_config.h @@ -606,6 +606,34 @@ public: return (dhcp4o6_port_); } + /// @brief Sets the server thread count. + /// + /// @param threads value of the server thread count + void setServerThreadCount(uint32_t threads) { + server_threads_ = threads; + } + + /// @brief Retrieves the server thread count. + /// + /// @return value of the server thread count + uint32_t getServerThreadCount() const { + return (server_threads_); + } + + /// @brief Sets the server max thread queue size. + /// + /// @param size max thread queue size + void setServerMaxThreadQueueSize(uint32_t size) { + server_max_thread_queue_size_ = size; + } + + /// @brief Retrieves the server max thread queue size. + /// + /// @return value of the max thread queue size + uint32_t getServerMaxThreadQueueSize() const { + return (server_max_thread_queue_size_); + } + /// @brief Returns pointer to the D2 client configuration D2ClientConfigPtr getD2ClientConfig() { return (d2_client_config_); @@ -824,6 +852,11 @@ private: /// this socket is bound and connected to this port and port + 1 uint16_t dhcp4o6_port_; + /// @brief The server thread count. + uint32_t server_threads_; + + /// @brief The server max thread queue size. + uint32_t server_max_thread_queue_size_; /// @brief Stores D2 client configuration D2ClientConfigPtr d2_client_config_; @@ -844,7 +877,7 @@ typedef boost::shared_ptr SrvConfigPtr; typedef boost::shared_ptr ConstSrvConfigPtr; //@} -} // namespace isc::dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // DHCPSRV_CONFIG_H diff --git a/src/lib/dhcpsrv/thread_pool.cc b/src/lib/dhcpsrv/thread_pool.cc new file mode 100644 index 0000000000..2b666e3e3c --- /dev/null +++ b/src/lib/dhcpsrv/thread_pool.cc @@ -0,0 +1,89 @@ +// Copyright (C) 2017-2019 Deutsche Telekom AG. +// +// Authors: Andrei Pavel +// Cristian Secareanu +// Razvan Becheriu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include + +using namespace std; + +namespace isc { +namespace dhcp { + +ThreadPool::ThreadPool() : exit_(true) { +} + +ThreadPool::~ThreadPool() { + destroy(); +} + +void ThreadPool::create(uint32_t worker_threads) { + LOG_INFO(dhcpsrv_logger, "Starting packet thread pool with %1 worker threads") + .arg(worker_threads); + if (!worker_threads) { + return; + } + destroy(); + queue_.create(); + exit_ = false; + for (int i = 0; i < worker_threads; ++i) { + worker_threads_.push_back(make_shared(&ThreadPool::threadRun, this)); + } + + LOG_INFO(dhcpsrv_logger, "Packet thread pool started"); +} + +void ThreadPool::destroy() { + LOG_INFO(dhcpsrv_logger, "Shutting down packet thread pool"); + exit_ = true; + queue_.destroy(); + for (auto thread : worker_threads_) { + thread->join(); + } + worker_threads_.clear(); + + LOG_INFO(dhcpsrv_logger, "Packet thread pool shut down"); +} + +void ThreadPool::add(WorkItemCallBack call_back) { + queue_.add(call_back); +} + +size_t ThreadPool::count() { + return queue_.count(); +} + +void ThreadPool::threadRun() { + thread::id th_id = this_thread::get_id(); + LOG_INFO(dhcpsrv_logger, "Packet thread pool new thread started. id: %1").arg(th_id); + + while (!exit_) { + WorkItemCallBack work_item; + if (queue_.get(work_item)) { + work_item(); + } + } + + LOG_INFO(dhcpsrv_logger, "Packet thread pool thread ended. id: %1").arg(th_id); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/thread_pool.h b/src/lib/dhcpsrv/thread_pool.h new file mode 100644 index 0000000000..4ee93287fc --- /dev/null +++ b/src/lib/dhcpsrv/thread_pool.h @@ -0,0 +1,135 @@ +// Copyright (C) 2017-2019 Deutsche Telekom AG. +// +// Authors: Andrei Pavel +// Cristian Secareanu +// Razvan Becheriu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THREAD_POOL_H +#define THREAD_POOL_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +template +struct ThreadPoolQueue { + ThreadPoolQueue() : exit_(true) { + } + + ~ThreadPoolQueue() { + } + + void add(WorkItem item) { + isc::util::thread::LockGuard lock(&mutex_); + if (exit_) { + return; + } + queue_.push(item); + // Notify get() so that it can effectively get a work item. + cv_.notify_all(); + } + + bool get(WorkItem& item) { + std::unique_lock lock(mutex_); + + while (!exit_) { + if (queue_.empty()) { + // Wait for add() or destroy(). + cv_.wait(lock); + continue; + } + + item = queue_.front(); + queue_.pop(); + return true; + } + + return false; + } + + size_t count() { + isc::util::thread::LockGuard lock(&mutex_); + return queue_.size(); + } + + void removeAll() { + isc::util::thread::LockGuard lock(&mutex_); + removeAllUnsafe(); + } + + void create() { + isc::util::thread::LockGuard lock(&mutex_); + exit_ = false; + } + + void destroy() { + isc::util::thread::LockGuard lock(&mutex_); + exit_ = true; + // Notify get() so that it can exit. + cv_.notify_all(); + removeAllUnsafe(); + } + +private: + /// @brief Has to be called in a mutex_-locked environment. + void removeAllUnsafe() { + while (queue_.size()) { + queue_.pop(); + } + } + + std::queue queue_; + std::mutex mutex_; + std::condition_variable cv_; + std::atomic_bool exit_; +}; + +struct ThreadPool { + using WorkItemCallBack = std::function; + + ThreadPool(); + ~ThreadPool(); + + void create(uint32_t worker_threads); + + void destroy(); + + void add(WorkItemCallBack call_back); + + size_t count(); + +private: + void threadRun(); + + std::list> worker_threads_; + ThreadPoolQueue queue_; + std::atomic_bool exit_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // THREAD_POOL_H diff --git a/src/lib/dhcpsrv/thread_resource_mgr.h b/src/lib/dhcpsrv/thread_resource_mgr.h new file mode 100644 index 0000000000..78011ce269 --- /dev/null +++ b/src/lib/dhcpsrv/thread_resource_mgr.h @@ -0,0 +1,42 @@ +// Copyright (C) 2019 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 THREAD_RESOURCE_MGR_H +#define THREAD_RESOURCE_MGR_H + +#include + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +template +class ThreadResourceMgr { + typedef boost::shared_ptr ResourcePtr; +public: + ResourcePtr resource() { + isc::util::thread::LockGuard lock(&mutex_); + auto id = std::this_thread::get_id(); + if (map_.find(id) != map_.end()) { + return map_[id]; + } + ResourcePtr result(new Resource()); + map_[id] = result; + return result; + } +private: + std::mutex mutex_; + std::unordered_map map_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // THREAD_RESOURCE_MGR_H diff --git a/src/lib/mysql/mysql_connection.h b/src/lib/mysql/mysql_connection.h index be0815c71b..118e388fdf 100644 --- a/src/lib/mysql/mysql_connection.h +++ b/src/lib/mysql/mysql_connection.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/lib/stats/stats_mgr.cc b/src/lib/stats/stats_mgr.cc index 25ad50086f..f4e5d4c3b9 100644 --- a/src/lib/stats/stats_mgr.cc +++ b/src/lib/stats/stats_mgr.cc @@ -10,12 +10,14 @@ #include #include #include +#include #include #include using namespace std; using namespace isc::data; using namespace isc::config; +using namespace isc::util::thread; namespace isc { namespace stats { @@ -27,53 +29,109 @@ StatsMgr& StatsMgr::instance() { StatsMgr::StatsMgr() : global_(new StatContext()) { - + mutex_.reset(new std::mutex()); } -void StatsMgr::setValue(const std::string& name, const int64_t value) { +void StatsMgr::setValue(const std::string& name, const int64_t value, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); setValueInternal(name, value); } -void StatsMgr::setValue(const std::string& name, const double value) { +void StatsMgr::setValue(const std::string& name, const double value, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); setValueInternal(name, value); } -void StatsMgr::setValue(const std::string& name, const StatsDuration& value) { +void StatsMgr::setValue(const std::string& name, const StatsDuration& value, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); setValueInternal(name, value); } -void StatsMgr::setValue(const std::string& name, const std::string& value) { + +void StatsMgr::setValue(const std::string& name, const std::string& value, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); setValueInternal(name, value); } -void StatsMgr::addValue(const std::string& name, const int64_t value) { +void StatsMgr::addValue(const std::string& name, const int64_t value, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); addValueInternal(name, value); } -void StatsMgr::addValue(const std::string& name, const double value) { +void StatsMgr::addValue(const std::string& name, const double value, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); addValueInternal(name, value); } -void StatsMgr::addValue(const std::string& name, const StatsDuration& value) { +void StatsMgr::addValue(const std::string& name, const StatsDuration& value, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); addValueInternal(name, value); } -void StatsMgr::addValue(const std::string& name, const std::string& value) { +void StatsMgr::addValue(const std::string& name, const std::string& value, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); addValueInternal(name, value); } -ObservationPtr StatsMgr::getObservation(const std::string& name) const { +ObservationPtr StatsMgr::getObservation(const std::string& name, bool lock) const { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); /// @todo: Implement contexts. // Currently we keep everything in a global context. return (global_->get(name)); } -void StatsMgr::addObservation(const ObservationPtr& stat) { +void StatsMgr::addObservation(const ObservationPtr& stat, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); /// @todo: Implement contexts. // Currently we keep everything in a global context. return (global_->add(stat)); } -bool StatsMgr::deleteObservation(const std::string& name) { +bool StatsMgr::deleteObservation(const std::string& name, bool lock) { + std::mutex* mlock = nullptr; + if (lock) { + mlock = mutex_.get(); + } + LockGuard lockGuard(mlock); /// @todo: Implement contexts. // Currently we keep everything in a global context. return (global_->del(name)); @@ -81,7 +139,8 @@ bool StatsMgr::deleteObservation(const std::string& name) { bool StatsMgr::setMaxSampleAge(const std::string& name, const StatsDuration& duration) { - ObservationPtr obs = getObservation(name); + LockGuard lock(mutex_.get()); + ObservationPtr obs = getObservation(name, false); if (obs) { obs->setMaxSampleAge(duration); return (true); @@ -92,7 +151,8 @@ bool StatsMgr::setMaxSampleAge(const std::string& name, bool StatsMgr::setMaxSampleCount(const std::string& name, uint32_t max_samples) { - ObservationPtr obs = getObservation(name); + LockGuard lock(mutex_.get()); + ObservationPtr obs = getObservation(name, false); if (obs) { obs->setMaxSampleCount(max_samples); return (true); @@ -102,6 +162,7 @@ bool StatsMgr::setMaxSampleCount(const std::string& name, } void StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) { + LockGuard lock(mutex_.get()); // Let's iterate over all stored statistics... for (std::map::iterator s = global_->stats_.begin(); s != global_->stats_.end(); ++s) { @@ -112,6 +173,7 @@ void StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) { } void StatsMgr::setMaxSampleCountAll(uint32_t max_samples) { + LockGuard lock(mutex_.get()); // Let's iterate over all stored statistics... for (std::map::iterator s = global_->stats_.begin(); s != global_->stats_.end(); ++s) { @@ -122,7 +184,8 @@ void StatsMgr::setMaxSampleCountAll(uint32_t max_samples) { } bool StatsMgr::reset(const std::string& name) { - ObservationPtr obs = getObservation(name); + LockGuard lock(mutex_.get()); + ObservationPtr obs = getObservation(name, false); if (obs) { obs->reset(); return (true); @@ -132,16 +195,19 @@ bool StatsMgr::reset(const std::string& name) { } bool StatsMgr::del(const std::string& name) { + LockGuard lock(mutex_.get()); return (global_->del(name)); } void StatsMgr::removeAll() { + LockGuard lock(mutex_.get()); global_->stats_.clear(); } isc::data::ConstElementPtr StatsMgr::get(const std::string& name) const { + LockGuard lock(mutex_.get()); isc::data::ElementPtr response = isc::data::Element::createMap(); // a map - ObservationPtr obs = getObservation(name); + ObservationPtr obs = getObservation(name, false); if (obs) { response->set(name, obs->getJSON()); // that contains observations } @@ -149,6 +215,7 @@ isc::data::ConstElementPtr StatsMgr::get(const std::string& name) const { } isc::data::ConstElementPtr StatsMgr::getAll() const { + LockGuard lock(mutex_.get()); isc::data::ElementPtr map = isc::data::Element::createMap(); // a map // Let's iterate over all stored statistics... @@ -162,6 +229,7 @@ isc::data::ConstElementPtr StatsMgr::getAll() const { } void StatsMgr::resetAll() { + LockGuard lock(mutex_.get()); // Let's iterate over all stored statistics... for (std::map::iterator s = global_->stats_.begin(); s != global_->stats_.end(); ++s) { @@ -172,7 +240,8 @@ void StatsMgr::resetAll() { } size_t StatsMgr::getSize(const std::string& name) const { - ObservationPtr obs = getObservation(name); + LockGuard lock(mutex_.get()); + ObservationPtr obs = getObservation(name, false); size_t size = 0; if (obs) { size = obs->getSize(); @@ -181,6 +250,7 @@ size_t StatsMgr::getSize(const std::string& name) const { } size_t StatsMgr::count() const { + LockGuard lock(mutex_.get()); return (global_->stats_.size()); } diff --git a/src/lib/stats/stats_mgr.h b/src/lib/stats/stats_mgr.h index 450d65a63b..79dccb0a07 100644 --- a/src/lib/stats/stats_mgr.h +++ b/src/lib/stats/stats_mgr.h @@ -10,8 +10,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -75,56 +77,56 @@ class StatsMgr : public boost::noncopyable { /// @param name name of the observation /// @param value integer value observed /// @throw InvalidStatType if statistic is not integer - void setValue(const std::string& name, const int64_t value); + void setValue(const std::string& name, const int64_t value, bool lock = true); /// @brief Records absolute floating point observation. /// /// @param name name of the observation /// @param value floating point value observed /// @throw InvalidStatType if statistic is not fp - void setValue(const std::string& name, const double value); + void setValue(const std::string& name, const double value, bool lock = true); /// @brief Records absolute duration observation. /// /// @param name name of the observation /// @param value duration value observed /// @throw InvalidStatType if statistic is not time duration - void setValue(const std::string& name, const StatsDuration& value); + void setValue(const std::string& name, const StatsDuration& value, bool lock = true); /// @brief Records absolute string observation. /// /// @param name name of the observation /// @param value string value observed /// @throw InvalidStatType if statistic is not a string - void setValue(const std::string& name, const std::string& value); + void setValue(const std::string& name, const std::string& value, bool lock = true); /// @brief Records incremental integer observation. /// /// @param name name of the observation /// @param value integer value observed /// @throw InvalidStatType if statistic is not integer - void addValue(const std::string& name, const int64_t value); + void addValue(const std::string& name, const int64_t value, bool lock = true); /// @brief Records incremental floating point observation. /// /// @param name name of the observation /// @param value floating point value observed /// @throw InvalidStatType if statistic is not fp - void addValue(const std::string& name, const double value); + void addValue(const std::string& name, const double value, bool lock = true); /// @brief Records incremental duration observation. /// /// @param name name of the observation /// @param value duration value observed /// @throw InvalidStatType if statistic is not time duration - void addValue(const std::string& name, const StatsDuration& value); + void addValue(const std::string& name, const StatsDuration& value, bool lock = true); /// @brief Records incremental string observation. /// /// @param name name of the observation /// @param value string value observed /// @throw InvalidStatType if statistic is not a string - void addValue(const std::string& name, const std::string& value); + void addValue(const std::string& name, const std::string& value, bool lock = true); /// @brief Determines maximum age of samples. /// @@ -222,7 +224,7 @@ class StatsMgr : public boost::noncopyable { /// Used in testing only. Production code should use @ref get() method. /// @param name name of the statistic /// @return Pointer to the Observation object - ObservationPtr getObservation(const std::string& name) const; + ObservationPtr getObservation(const std::string& name, bool lock = true) const; /// @brief Generates statistic name in a given context /// @@ -447,14 +449,13 @@ private: /// @throw InvalidStatType is statistic exists and has a different type. template void setValueInternal(const std::string& name, DataType value) { - // If we want to log each observation, here would be the best place for it. - ObservationPtr stat = getObservation(name); + ObservationPtr stat = getObservation(name, false); if (stat) { stat->setValue(value); } else { stat.reset(new Observation(name, value)); - addObservation(stat); + addObservation(stat, false); } } @@ -472,14 +473,13 @@ private: /// @throw InvalidStatType is statistic exists and has a different type. template void addValueInternal(const std::string& name, DataType value) { - // If we want to log each observation, here would be the best place for it. - ObservationPtr existing = getObservation(name); + ObservationPtr existing = getObservation(name, false); if (!existing) { // We tried to add to a non-existing statistic. We can recover from // that. Simply add the new incremental value as a new statistic and // we're done. - setValue(name, value); + setValue(name, value, false); return; } else { // Let's hope it is of correct type. If not, the underlying @@ -495,7 +495,7 @@ private: /// That's an utility method used by public @ref setValue() and /// @ref addValue() methods. /// @param stat observation - void addObservation(const ObservationPtr& stat); + void addObservation(const ObservationPtr& stat, bool lock = true); /// @private @@ -503,7 +503,7 @@ private: /// /// @param name of the statistic to be deleted /// @return true if deleted, false if not found - bool deleteObservation(const std::string& name); + bool deleteObservation(const std::string& name, bool lock = true); /// @brief Utility method that attempts to extract statistic name /// @@ -562,6 +562,8 @@ private: // This is a global context. All statistics will initially be stored here. StatContextPtr global_; + + boost::scoped_ptr mutex_; }; }; diff --git a/src/lib/util/threads/lock_guard.h b/src/lib/util/threads/lock_guard.h new file mode 100644 index 0000000000..6c949a29f6 --- /dev/null +++ b/src/lib/util/threads/lock_guard.h @@ -0,0 +1,39 @@ +#ifndef LOCK_GUARD_H +#define LOCK_GUARD_H + +#include + +namespace isc { +namespace util { +namespace thread { + +template +class LockGuard { +public: + LockGuard(Lock* lock) : lk_(lock) { + if (lk_) { + lk_->lock(); + } + } + + ~LockGuard() { + if (lk_) { + lk_->unlock(); + } + } + + LockGuard(const LockGuard&) = delete; + LockGuard& operator=(const LockGuard&) = delete; + + LockGuard(LockGuard&&) = delete; + LockGuard& operator=(LockGuard&&) = delete; + +private: + Lock* lk_; +}; + +} // namespace thread +} // namespace util +} // namespace isc + +#endif // LOCK_GUARD_H