for details. Also, see :ref:`lease-reclamation` for general
information about the processing of expired leases (lease reclamation).
-.. isccmd:: interface-list
-.. _command-interface-list:
-
-The ``interface-list`` Command
-------------------------------
-
-The :isccmd:`interface-list` command retrieves the list of detected interfaces.
-This command does not take any parameters.
-
-.. isccmd:: interface-redetect
-.. _command-interface-redetect:
-
-The ``interface-redetect`` Command
-----------------------------------
-
-The :isccmd:`interface-redetect` command retrieves the list of detected interfaces
-after performing a re-detect procedure which only adds newly discovered interfaces,
-without removing any previously detected interfaces.
-This command does not take any parameters."
-
.. isccmd:: interface-add
.. _command-interface-add:
to process DHCP traffic.
The command takes as parameter the list of interfaces with respective
addresses (if specified) on which the server should start listening for
-DHCP traffic.
+DHCP traffic. If there are duplicate entries in the command list or they
+are duplicating the entries in the running configuration, these entries
+will have no effect on the new configuration.
.. note::
::
- {"result": 0, "text": "Configuration successful." }
+ { "result": 0, "text": "Interface configuration successfully updated." }
The updated configuration can also be retrieved using the :isccmd:`config-get`
command (inside the "interfaces-config" configuration map).
+.. isccmd:: interface-list
+.. _command-interface-list:
+
+The ``interface-list`` Command
+------------------------------
+
+The :isccmd:`interface-list` command retrieves the list of detected interfaces.
+This command does not take any parameters.
+
+.. isccmd:: interface-redetect
+.. _command-interface-redetect:
+
+The ``interface-redetect`` Command
+----------------------------------
+
+The :isccmd:`interface-redetect` command retrieves the list of detected interfaces
+after performing a re-detect procedure which only adds newly discovered interfaces,
+without removing any previously detected interfaces.
+This command does not take any parameters."
+
.. isccmd:: list-commands
.. _command-list-commands:
- :isccmd:`config-write`
- :isccmd:`dhcp-disable`
- :isccmd:`dhcp-enable`
+- :isccmd:`interface-add`
- :isccmd:`interface-list`
- :isccmd:`interface-redetect`
-- :isccmd:`interface-add`
- :isccmd:`leases-reclaim`
- :isccmd:`list-commands`
- :isccmd:`shutdown`
- :isccmd:`config-write`
- :isccmd:`dhcp-disable`
- :isccmd:`dhcp-enable`
+- :isccmd:`interface-add`
- :isccmd:`interface-list`
- :isccmd:`interface-redetect`
-- :isccmd:`interface-add`
- :isccmd:`leases-reclaim`
- :isccmd:`list-commands`
- :isccmd:`shutdown`
ConstElementPtr
ControlledDhcpv4Srv::commandInterfaceListHandler(const std::string&,
ConstElementPtr) {
- // stop thread pool (if running)
- MultiThreadingCriticalSection cs;
+ if (!IfaceMgr::instance().isMainThread()) {
+ return (isc::config::createAnswer(CONTROL_RESULT_ERROR,
+ "Illegal operation executing 'interface-list' on a different thread than main thread"));
+ }
ElementPtr ifaces = Element::createMap();
std::string message;
bool error = false;
ConstElementPtr
ControlledDhcpv4Srv::commandInterfaceRedetectHandler(const std::string&,
ConstElementPtr args) {
- // stop thread pool (if running)
- MultiThreadingCriticalSection cs;
+ if (!IfaceMgr::instance().isMainThread()) {
+ return (isc::config::createAnswer(CONTROL_RESULT_ERROR,
+ "Illegal operation executing 'interface-redetect' on a different thread than main thread"));
+ }
std::string message;
bool error = false;
try {
+ // stop thread pool (if running)
+ MultiThreadingCriticalSection cs;
IfaceMgr::instance().detectIfaces(true);
} catch (const std::exception& ex) {
error = true;
ConstElementPtr
ControlledDhcpv4Srv::commandInterfaceAddHandler(const std::string&,
ConstElementPtr args) {
- // stop thread pool (if running)
- MultiThreadingCriticalSection cs;
+ if (!IfaceMgr::instance().isMainThread()) {
+ return (isc::config::createAnswer(CONTROL_RESULT_ERROR,
+ "Illegal operation executing 'interface-add' on a different thread than main thread"));
+ }
string message;
ConstElementPtr ifaces_config;
if (!args) {
}
bool error = false;
try {
- ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(args);
- CfgIfacePtr running_cfg_iface = CfgMgr::instance().getCurrentCfg()->getCfgIface();
- ElementPtr mutable_running_cfg = running_cfg_iface->toElement();
- auto const& element_empty = [](ElementPtr&) {
- return (true);
- };
- auto const& element_match_any = [](ElementPtr&, ElementPtr&) -> bool {
- return (true);
- };
- auto const& element_match = [](ElementPtr& left, ElementPtr& right) -> bool {
- return (left->stringValue() == right->stringValue());
- };
- auto const& element_is_key = [](const std::string& key) -> bool {
- return (key == "interfaces");
- };
- isc::data::HierarchyDescriptor hierarchy = {
- { { "interfaces-config", { element_match_any, element_empty, element_is_key } } },
- { { "interfaces", { element_match, element_empty, element_is_key } } }
- };
- mergeDiffAdd(mutable_running_cfg, mutable_cfg, hierarchy, "interfaces");
+ CfgIfacePtr running_cfg = CfgMgr::instance().getCurrentCfg()->getCfgIface();
+ ElementPtr ifaces = Element::createList();
+ std::set<std::string> seen;
+ auto running_ifaces = running_cfg->toElement()->get("interfaces");
+ if (running_ifaces && (running_ifaces->getType() == Element::list)) {
+ for (auto const& item : running_ifaces->listValue()) {
+ seen.insert(item->stringValue());
+ ifaces->add(item);
+ }
+ }
+ for (auto const& item : ifaces_config->listValue()) {
+ auto const& str = item->stringValue();
+ if (seen.find(item->stringValue()) != seen.end()) {
+ continue;
+ }
+ seen.insert(item->stringValue());
+ ifaces->add(item);
+ }
IfacesConfigParser parser(AF_INET, true);
CfgIfacePtr cfg_iface(new CfgIface());
- parser.parseInterfacesList(cfg_iface, mutable_running_cfg->get("interfaces"));
- CfgMgr::instance().getCurrentCfg()->getCfgIface()->update(*cfg_iface);
- running_cfg_iface->triggerOpenSocketsWithRetry(AF_INET, getServerPort(), useBroadcast());
+ parser.parseInterfacesList(cfg_iface, ifaces);
+ running_cfg->update(*cfg_iface);
+ running_cfg->triggerOpenSocketsWithRetry(AF_INET, getServerPort(), useBroadcast());
} catch (const std::exception& ex) {
error = true;
message = ex.what();
ConstElementPtr
ControlledDhcpv6Srv::commandInterfaceListHandler(const std::string&,
ConstElementPtr) {
- // stop thread pool (if running)
- MultiThreadingCriticalSection cs;
+ if (!IfaceMgr::instance().isMainThread()) {
+ return (isc::config::createAnswer(CONTROL_RESULT_ERROR,
+ "Illegal operation executing 'interface-list' on a different thread than main thread"));
+ }
ElementPtr ifaces = Element::createMap();
std::string message;
bool error = false;
ConstElementPtr
ControlledDhcpv6Srv::commandInterfaceRedetectHandler(const std::string&,
ConstElementPtr args) {
- // stop thread pool (if running)
- MultiThreadingCriticalSection cs;
+ if (!IfaceMgr::instance().isMainThread()) {
+ return (isc::config::createAnswer(CONTROL_RESULT_ERROR,
+ "Illegal operation executing 'interface-redetect' on a different thread than main thread"));
+ }
std::string message;
bool error = false;
try {
+ // stop thread pool (if running)
+ MultiThreadingCriticalSection cs;
IfaceMgr::instance().detectIfaces(true);
} catch (const std::exception& ex) {
error = true;
ConstElementPtr
ControlledDhcpv6Srv::commandInterfaceAddHandler(const std::string&,
ConstElementPtr args) {
- // stop thread pool (if running)
- MultiThreadingCriticalSection cs;
+ if (!IfaceMgr::instance().isMainThread()) {
+ return (isc::config::createAnswer(CONTROL_RESULT_ERROR,
+ "Illegal operation executing 'interface-add' on a different thread than main thread"));
+ }
string message;
ConstElementPtr ifaces_config;
if (!args) {
}
bool error = false;
try {
- ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(args);
- CfgIfacePtr running_cfg_iface = CfgMgr::instance().getCurrentCfg()->getCfgIface();
- ElementPtr mutable_running_cfg = running_cfg_iface->toElement();
- auto const& element_empty = [](ElementPtr&) {
- return (true);
- };
- auto const& element_match_any = [](ElementPtr&, ElementPtr&) -> bool {
- return (true);
- };
- auto const& element_match = [](ElementPtr& left, ElementPtr& right) -> bool {
- return (left->stringValue() == right->stringValue());
- };
- auto const& element_is_key = [](const std::string& key) -> bool {
- return (key == "interfaces");
- };
- isc::data::HierarchyDescriptor hierarchy = {
- { { "interfaces-config", { element_match_any, element_empty, element_is_key } } },
- { { "interfaces", { element_match, element_empty, element_is_key } } }
- };
- mergeDiffAdd(mutable_running_cfg, mutable_cfg, hierarchy, "interfaces");
+ CfgIfacePtr running_cfg = CfgMgr::instance().getCurrentCfg()->getCfgIface();
+ ElementPtr ifaces = Element::createList();
+ std::set<std::string> seen;
+ auto running_ifaces = running_cfg->toElement()->get("interfaces");
+ if (running_ifaces && (running_ifaces->getType() == Element::list)) {
+ for (auto const& item : running_ifaces->listValue()) {
+ seen.insert(item->stringValue());
+ ifaces->add(item);
+ }
+ }
+ for (auto const& item : ifaces_config->listValue()) {
+ auto const& str = item->stringValue();
+ if (seen.find(item->stringValue()) != seen.end()) {
+ continue;
+ }
+ seen.insert(item->stringValue());
+ ifaces->add(item);
+ }
IfacesConfigParser parser(AF_INET6, true);
CfgIfacePtr cfg_iface(new CfgIface());
- parser.parseInterfacesList(cfg_iface, mutable_running_cfg->get("interfaces"));
- CfgMgr::instance().getCurrentCfg()->getCfgIface()->update(*cfg_iface);
- running_cfg_iface->triggerOpenSocketsWithRetry(AF_INET6, getServerPort());
+ parser.parseInterfacesList(cfg_iface, ifaces);
+ running_cfg->update(*cfg_iface);
+ running_cfg->triggerOpenSocketsWithRetry(AF_INET6, getServerPort());
} catch (const std::exception& ex) {
error = true;
message = ex.what();
check_thread_id_ = check;
}
+ /// @brief Check if called from the main thread.
+ ///
+ /// @return true if the current thread is the main thread, false otherwise.
+ bool isMainThread() const {
+ return (std::this_thread::get_id() == id_);
+ }
+
/// @brief Allows or disallows the loopback interface
///
/// By default the loopback interface is not considered when opening
uint16_t family_;
};
+/// @brief RAII class creating a critical section for the receiver thread.
+class ReceiverCriticalSection : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Entering the critical section: if running, the receiver
+ /// is stopped not clearing the packet queue.
+ ReceiverCriticalSection(IfaceMgr& iface_mgr)
+ : iface_mgr_(iface_mgr), is_running_(iface_mgr.isDHCPReceiverRunning()) {
+ if (is_running_) {
+ iface_mgr_.stopDHCPReceiver(false);
+ }
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Leaving the critical section: if it was running, the receiver
+ /// is started.
+ ~ReceiverCriticalSection() {
+ if (is_running_) {
+ auto family = iface_mgr_.getFamily();
+ iface_mgr_.startDHCPReceiver(family);
+ }
+ }
+
+private:
+ /// @brief The IfaceMgr instance.
+ IfaceMgr& iface_mgr_;
+
+ /// @brief Is running flag.
+ bool is_running_;
+};
+
} // namespace isc::dhcp
} // namespace isc
/// This is a BSD specific interface detection method.
void
IfaceMgr::detectIfaces(bool update_only) {
- IfaceMgr* mgr_p = 0;
- if (isDHCPReceiverRunning()) {
- mgr_p = this;
- stopDHCPReceiver(false);
- }
- std::unique_ptr<void, void(*)(void*)> p(static_cast<void*>(mgr_p), [](void* m) {
- if (m) {
- IfaceMgr* mgr = reinterpret_cast<IfaceMgr*>(m);
- mgr->startDHCPReceiver(mgr->getFamily());
- }
- });
+ ReceiverCriticalSection rcs(*this);
if (detect_callback_) {
if (!detect_callback_(update_only)) {
return;
/// Uses the socket-based netlink protocol to retrieve the list of interfaces
/// from the Linux kernel.
void IfaceMgr::detectIfaces(bool update_only) {
- IfaceMgr* mgr_p = 0;
- if (isDHCPReceiverRunning()) {
- mgr_p = this;
- stopDHCPReceiver(false);
- }
- std::unique_ptr<void, void(*)(void*)> p(static_cast<void*>(mgr_p), [](void* m) {
- if (m) {
- IfaceMgr* mgr = reinterpret_cast<IfaceMgr*>(m);
- mgr->startDHCPReceiver(mgr->getFamily());
- }
- });
+ ReceiverCriticalSection rcs(*this);
if (detect_callback_) {
if (!detect_callback_(update_only)) {
return;
testReceive6RotateIfaces(false);
}
+TEST_F(IfaceMgrTest, receiverCS) {
+ ASSERT_FALSE(IfaceMgr::instance().isDHCPReceiverRunning());
+ {
+ ReceiverCriticalSection rcs(IfaceMgr::instance());
+ ASSERT_FALSE(IfaceMgr::instance().isDHCPReceiverRunning());
+ }
+ ASSERT_FALSE(IfaceMgr::instance().isDHCPReceiverRunning());
+ bool queue_enabled = false;
+ data::ConstElementPtr config = makeQueueConfig(PacketQueueMgr4::DEFAULT_QUEUE_TYPE4, 500);
+ ASSERT_NO_THROW(queue_enabled = IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, config));
+ ASSERT_TRUE(queue_enabled);
+
+ // Thread should only start when there is a packet queue.
+ ASSERT_NO_THROW(IfaceMgr::instance().startDHCPReceiver(AF_INET));
+ ASSERT_TRUE(IfaceMgr::instance().isDHCPReceiverRunning());
+ {
+ ReceiverCriticalSection rcs(IfaceMgr::instance());
+ ASSERT_FALSE(IfaceMgr::instance().isDHCPReceiverRunning());
+ }
+ ASSERT_TRUE(IfaceMgr::instance().isDHCPReceiverRunning());
+}
+
}