]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3144] implemented commands
authorRazvan Becheriu <razvan@isc.org>
Tue, 31 Mar 2026 19:55:14 +0000 (22:55 +0300)
committerRazvan Becheriu <razvan@isc.org>
Mon, 18 May 2026 11:22:02 +0000 (14:22 +0300)
21 files changed:
doc/sphinx/arm/dhcp4-srv.rst
doc/sphinx/arm/dhcp6-srv.rst
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/ctrl_dhcp4_srv.h
src/bin/dhcp4/main.cc
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/ctrl_dhcp6_srv.h
src/bin/dhcp6/main.cc
src/bin/shell/tests/dhcp4_basic_auth_tests.sh.in
src/bin/shell/tests/dhcp6_basic_auth_tests.sh.in
src/bin/shell/tests/shell_dhcp4_process_tests.sh.in
src/bin/shell/tests/shell_dhcp6_process_tests.sh.in
src/bin/shell/tests/tls_dhcp4_process_tests.sh.in
src/bin/shell/tests/tls_dhcp6_process_tests.sh.in
src/lib/dhcp/iface_mgr.cc
src/lib/dhcp/iface_mgr.h
src/lib/dhcpsrv/cfg_iface.cc
src/lib/dhcpsrv/cfg_iface.h
src/share/api/interface-list.json
src/share/api/interface-redetect.json
src/share/api/interface-use.json

index 3f8d4cd7142a3d534b29f7d96533029b15e311a1..ba5dc6b222f19e8cf4ddbef8307d620638d78fff 100644 (file)
@@ -8005,6 +8005,9 @@ The DHCPv4 server supports the following operational commands:
 - :isccmd:`config-write`
 - :isccmd:`dhcp-disable`
 - :isccmd:`dhcp-enable`
+- :isccmd:`interface-list`
+- :isccmd:`interface-redetect`
+- :isccmd:`interface-use`
 - :isccmd:`leases-reclaim`
 - :isccmd:`list-commands`
 - :isccmd:`shutdown`
index 3b546af438ac03d059f801dd28689bec98b0cf64..aab3bc79512abc9d5d89b16054c522c95a09e60b 100644 (file)
@@ -7984,6 +7984,9 @@ The DHCPv6 server supports the following operational commands:
 - :isccmd:`config-write`
 - :isccmd:`dhcp-disable`
 - :isccmd:`dhcp-enable`
+- :isccmd:`interface-list`
+- :isccmd:`interface-redetect`
+- :isccmd:`interface-use`
 - :isccmd:`leases-reclaim`
 - :isccmd:`list-commands`
 - :isccmd:`shutdown`
index 95064ec729802d81e180cbc6427a6a332eb806f7..2f4678702adcc44c9953222daf50d5b1c43a719c 100644 (file)
@@ -28,6 +28,7 @@
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
+#include <dhcpsrv/parsers/ifaces_config_parser.h>
 #include <hooks/hooks.h>
 #include <hooks/hooks_manager.h>
 #include <process/cfgrpt/config_report.h>
@@ -675,6 +676,104 @@ ControlledDhcpv4Srv::commandDhcpEnableHandler(const std::string&,
     return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
 }
 
+ConstElementPtr
+ControlledDhcpv4Srv::commandInterfaceListHandler(const std::string&,
+                                                 ConstElementPtr) {
+    ElementPtr ifaces = Element::createMap();
+    std::string message;
+    try {
+        ifaces->set("interfaces", IfaceMgr::instance().ifacesToElement());
+    } catch (std::exception& ex) {
+        message = ex.what();
+    } catch (...) {
+        message = "unknown error";
+    }
+
+    ostringstream msg;
+    if (message.empty()) {
+        msg << IfaceMgr::instance().getIfaces().size()
+            << " interfaces detected.";
+        return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, msg.str(), ifaces));
+    } else {
+        msg << "Unexpected error while retrieving the list of detected interfaces: " << message;
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, msg.str()));
+    }
+}
+
+ConstElementPtr
+ControlledDhcpv4Srv::commandInterfaceRedetectHandler(const std::string&,
+                                                     ConstElementPtr args) {
+    std::string message;
+    try {
+        IfaceMgr::instance().detectIfaces(true);
+    } catch (std::exception& ex) {
+        message = ex.what();
+    } catch (...) {
+        message = "unknown error";
+    }
+
+    ostringstream msg;
+    if (message.empty()) {
+        return (ControlledDhcpv4Srv::commandInterfaceListHandler("", args));
+    } else {
+        msg << "Unexpected error while retrieving the list of detected interfaces: " << message;
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, msg.str()));
+    }
+}
+
+ConstElementPtr
+ControlledDhcpv4Srv::commandInterfaceUseHandler(const std::string&,
+                                                ConstElementPtr args) {
+    string message;
+    ConstElementPtr ifaces_config;
+    if (!args) {
+        message = "Missing mandatory 'arguments' parameter.";
+    } else {
+        if (args->getType() != Element::map) {
+            message = "arguments for the 'interface-use' command must be a map";
+        } else {
+            ifaces_config = args->get("interfaces");
+            if (!ifaces_config) {
+                message = "Missing mandatory 'interfaces' map parameter in 'arguments'.";
+            }
+            auto map = args->mapValue();
+            for (auto const& key : map) {
+                if (key.first != "interfaces") {
+                    message = "Unsupported '" + key.first + "' map parameter in 'arguments'.";
+                    break;
+                }
+            }
+        }
+    }
+
+    if (!message.empty()) {
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, message));
+    }
+    try {
+        ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(args);
+        mutable_cfg->set("re-detect", Element::create(false));
+        IfacesConfigParser parser(AF_INET, true);
+        CfgIfacePtr cfg_iface(new CfgIface());
+        parser.parse(cfg_iface, args);
+        CfgIfacePtr running_cfg_iface = CfgMgr::instance().getCurrentCfg()->getCfgIface();
+        if (running_cfg_iface->merge(*cfg_iface, AF_INET)) {
+            running_cfg_iface->triggerOpenSocketsWithRetry(AF_INET, getServerPort(), useBroadcast());
+        }
+    } catch (std::exception& ex) {
+        message = ex.what();
+    } catch (...) {
+        message = "unknown error";
+    }
+
+    ostringstream msg;
+    if (message.empty()) {
+        return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful."));
+    } else {
+        msg << "Unexpected error while updating used interfaces: " << message;
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, msg.str()));
+    }
+}
+
 ConstElementPtr
 ControlledDhcpv4Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
     ElementPtr extended = Element::create(Dhcpv4Srv::getVersion(true));
@@ -1527,6 +1626,15 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_P
     CommandMgr::instance().registerCommand("dhcp-disable",
         std::bind(&ControlledDhcpv4Srv::commandDhcpDisableHandler, this, ph::_1, ph::_2));
 
+    CommandMgr::instance().registerCommand("interface-list",
+        std::bind(&ControlledDhcpv4Srv::commandInterfaceListHandler, this, ph::_1, ph::_2));
+
+    CommandMgr::instance().registerCommand("interface-redetect",
+        std::bind(&ControlledDhcpv4Srv::commandInterfaceRedetectHandler, this, ph::_1, ph::_2));
+
+    CommandMgr::instance().registerCommand("interface-use",
+        std::bind(&ControlledDhcpv4Srv::commandInterfaceUseHandler, this, ph::_1, ph::_2));
+
     CommandMgr::instance().registerCommand("kea-lfc-start",
         std::bind(&ControlledDhcpv4Srv::commandLfcStartHandler, this, ph::_1, ph::_2));
 
@@ -1623,6 +1731,9 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
         CommandMgr::instance().deregisterCommand("config-write");
         CommandMgr::instance().deregisterCommand("dhcp-disable");
         CommandMgr::instance().deregisterCommand("dhcp-enable");
+        CommandMgr::instance().deregisterCommand("interface-list");
+        CommandMgr::instance().deregisterCommand("interface-redetect");
+        CommandMgr::instance().deregisterCommand("interface-use");
         CommandMgr::instance().deregisterCommand("kea-lfc-start");
         CommandMgr::instance().deregisterCommand("leases-reclaim");
         CommandMgr::instance().deregisterCommand("subnet4-select-test");
index 445a5df0269c61fa322b47ce3dea320bf9f2817a..f3c5b856f2feca152ac7181351afadbcdd410d49 100644 (file)
@@ -238,6 +238,42 @@ private:
     commandDhcpEnableHandler(const std::string& command,
                              isc::data::ConstElementPtr args);
 
+    /// @brief Handler for processing 'interface-list' command
+    ///
+    /// This handler processes interface-list command.
+    ///
+    /// @param command (parameter ignored)
+    /// @param args arguments for the command (ignored).
+    ///
+    /// @return status of the command with the result
+    isc::data::ConstElementPtr
+    commandInterfaceListHandler(const std::string& command,
+                                isc::data::ConstElementPtr args);
+
+    /// @brief Handler for processing 'interface-redetect' command
+    ///
+    /// This handler processes interface-redetect command.
+    ///
+    /// @param command (parameter ignored)
+    /// @param args arguments for the command (ignored).
+    ///
+    /// @return status of the command with the result
+    isc::data::ConstElementPtr
+    commandInterfaceRedetectHandler(const std::string& command,
+                                    isc::data::ConstElementPtr args);
+
+    /// @brief Handler for processing 'interface-use' command
+    ///
+    /// This handler processes interface-use command.
+    ///
+    /// @param command (parameter ignored)
+    /// @param args arguments for the command.
+    ///
+    /// @return status of the command with the result
+    isc::data::ConstElementPtr
+    commandInterfaceUseHandler(const std::string& command,
+                               isc::data::ConstElementPtr args);
+
     /// @Brief handler for processing 'version-get' command
     ///
     /// This handler processes version-get command, which returns
@@ -326,7 +362,7 @@ private:
     /// @param command (parameter ignored)
     /// @param args (ignored)
     ///
-    /// @return status of the command/
+    /// @return status of the command.
     isc::data::ConstElementPtr
     commandConfigBackendPullHandler(const std::string& command,
                                     isc::data::ConstElementPtr args);
index 166ff75a62609b4fb07d4c7f9b407ea92f99bee9..47814056440d8e6726bb1081d66d46dc4429f708 100644 (file)
@@ -90,6 +90,7 @@ main(int argc, char* argv[]) {
     std::string config_file("");
 
     // This is the DHCPv4 server
+    IfaceMgr::instance().setFamily(AF_INET);
     CfgMgr::instance().setFamily(AF_INET);
 
     while ((ch = getopt(argc, argv, "dvVWc:p:P:t:T:X")) != -1) {
index 99b982e2e8b03f0f3174310240c8b962dc821b23..d373392e582c34c7d7f2340458982b75f91cc13a 100644 (file)
@@ -28,6 +28,7 @@
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
+#include <dhcpsrv/parsers/ifaces_config_parser.h>
 #include <hooks/hooks.h>
 #include <hooks/hooks_manager.h>
 #include <process/cfgrpt/config_report.h>
@@ -678,6 +679,104 @@ ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&,
     return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
 }
 
+ConstElementPtr
+ControlledDhcpv6Srv::commandInterfaceListHandler(const std::string&,
+                                                 ConstElementPtr) {
+    ElementPtr ifaces = Element::createMap();
+    std::string message;
+    try {
+        ifaces->set("interfaces", IfaceMgr::instance().ifacesToElement());
+    } catch (std::exception& ex) {
+        message = ex.what();
+    } catch (...) {
+        message = "unknown error";
+    }
+
+    ostringstream msg;
+    if (message.empty()) {
+        msg << IfaceMgr::instance().getIfaces().size()
+            << " interfaces detected.";
+        return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, msg.str(), ifaces));
+    } else {
+        msg << "Unexpected error while retrieving the list of detected interfaces: " << message;
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, msg.str()));
+    }
+}
+
+ConstElementPtr
+ControlledDhcpv6Srv::commandInterfaceRedetectHandler(const std::string&,
+                                                     ConstElementPtr args) {
+    std::string message;
+    try {
+        IfaceMgr::instance().detectIfaces(true);
+    } catch (std::exception& ex) {
+        message = ex.what();
+    } catch (...) {
+        message = "unknown error";
+    }
+
+    ostringstream msg;
+    if (message.empty()) {
+        return (ControlledDhcpv6Srv::commandInterfaceListHandler("", args));
+    } else {
+        msg << "Unexpected error while retrieving the list of detected interfaces: " << message;
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, msg.str()));
+    }
+}
+
+ConstElementPtr
+ControlledDhcpv6Srv::commandInterfaceUseHandler(const std::string&,
+                                                ConstElementPtr args) {
+    string message;
+    ConstElementPtr ifaces_config;
+    if (!args) {
+        message = "Missing mandatory 'arguments' parameter.";
+    } else {
+        if (args->getType() != Element::map) {
+            message = "arguments for the 'interface-use' command must be a map";
+        } else {
+            ifaces_config = args->get("interfaces");
+            if (!ifaces_config) {
+                message = "Missing mandatory 'interfaces' map parameter in 'arguments'.";
+            }
+            auto map = args->mapValue();
+            for (auto const& key : map) {
+                if (key.first != "interfaces") {
+                    message = "Unsupported '" + key.first + "' map parameter in 'arguments'.";
+                    break;
+                }
+            }
+        }
+    }
+
+    if (!message.empty()) {
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, message));
+    }
+    try {
+        ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(args);
+        mutable_cfg->set("re-detect", Element::create(false));
+        IfacesConfigParser parser(AF_INET6, true);
+        CfgIfacePtr cfg_iface(new CfgIface());
+        parser.parse(cfg_iface, args);
+        CfgIfacePtr running_cfg_iface = CfgMgr::instance().getCurrentCfg()->getCfgIface();
+        if (running_cfg_iface->merge(*cfg_iface, AF_INET6)) {
+            running_cfg_iface->triggerOpenSocketsWithRetry(AF_INET6, getServerPort());
+        }
+    } catch (std::exception& ex) {
+        message = ex.what();
+    } catch (...) {
+        message = "unknown error";
+    }
+
+    ostringstream msg;
+    if (message.empty()) {
+        return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful."));
+    } else {
+        msg << "Unexpected error while updating used interfaces: " << message;
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, msg.str()));
+    }
+}
+
 ConstElementPtr
 ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
     ElementPtr extended = Element::create(Dhcpv6Srv::getVersion(true));
@@ -1317,6 +1416,15 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port /*= DHCP6_SERVER_P
     CommandMgr::instance().registerCommand("dhcp-disable",
         std::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, ph::_1, ph::_2));
 
+    CommandMgr::instance().registerCommand("interface-list",
+        std::bind(&ControlledDhcpv6Srv::commandInterfaceListHandler, this, ph::_1, ph::_2));
+
+    CommandMgr::instance().registerCommand("interface-redetect",
+        std::bind(&ControlledDhcpv6Srv::commandInterfaceRedetectHandler, this, ph::_1, ph::_2));
+
+    CommandMgr::instance().registerCommand("interface-use",
+        std::bind(&ControlledDhcpv6Srv::commandInterfaceUseHandler, this, ph::_1, ph::_2));
+
     CommandMgr::instance().registerCommand("kea-lfc-start",
         std::bind(&ControlledDhcpv6Srv::commandLfcStartHandler, this, ph::_1, ph::_2));
 
@@ -1410,6 +1518,9 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
         CommandMgr::instance().deregisterCommand("config-write");
         CommandMgr::instance().deregisterCommand("dhcp-disable");
         CommandMgr::instance().deregisterCommand("dhcp-enable");
+        CommandMgr::instance().deregisterCommand("interface-list");
+        CommandMgr::instance().deregisterCommand("interface-redetect");
+        CommandMgr::instance().deregisterCommand("interface-use");
         CommandMgr::instance().deregisterCommand("kea-lfc-start");
         CommandMgr::instance().deregisterCommand("leases-reclaim");
         CommandMgr::instance().deregisterCommand("subnet6-select-test");
index 23183fa05cdc9643e711cc0dfce41b3189f0d106..ccdcde3ea6c51c7db181209b94e2f93365bc3ae6 100644 (file)
@@ -238,6 +238,42 @@ private:
     commandDhcpEnableHandler(const std::string& command,
                              isc::data::ConstElementPtr args);
 
+    /// @brief Handler for processing 'interface-list' command
+    ///
+    /// This handler processes interface-list command.
+    ///
+    /// @param command (parameter ignored)
+    /// @param args arguments for the command (ignored).
+    ///
+    /// @return status of the command with the result
+    isc::data::ConstElementPtr
+    commandInterfaceListHandler(const std::string& command,
+                                isc::data::ConstElementPtr args);
+
+    /// @brief Handler for processing 'interface-redetect' command
+    ///
+    /// This handler processes interface-redetect command.
+    ///
+    /// @param command (parameter ignored)
+    /// @param args arguments for the command (ignored).
+    ///
+    /// @return status of the command with the result
+    isc::data::ConstElementPtr
+    commandInterfaceRedetectHandler(const std::string& command,
+                                    isc::data::ConstElementPtr args);
+
+    /// @brief Handler for processing 'interface-use' command
+    ///
+    /// This handler processes interface-use command.
+    ///
+    /// @param command (parameter ignored)
+    /// @param args arguments for the command.
+    ///
+    /// @return status of the command with the result
+    isc::data::ConstElementPtr
+    commandInterfaceUseHandler(const std::string& command,
+                               isc::data::ConstElementPtr args);
+
     /// @Brief handler for processing 'version-get' command
     ///
     /// This handler processes version-get command, which returns
@@ -313,7 +349,7 @@ private:
     /// @param command (parameter ignored)
     /// @param args (ignored)
     ///
-    /// @return status of the command/
+    /// @return status of the command.
     isc::data::ConstElementPtr
     commandConfigBackendPullHandler(const std::string& command,
                                     isc::data::ConstElementPtr args);
index 4f163213ec9f29ebbfe107cbf73c769ecb2d49fe..3a119e71c064bca50730af1cec23511bc23834fd 100644 (file)
@@ -90,6 +90,7 @@ main(int argc, char* argv[]) {
     std::string config_file("");
 
     // This is the DHCPv6 server
+    IfaceMgr::instance().setFamily(AF_INET6);
     CfgMgr::instance().setFamily(AF_INET6);
 
     while ((ch = getopt(argc, argv, "dvVWc:p:P:t:T:X")) != -1) {
index 90276ede37fc1c085afe19886deac0f43494074d..b2cd1a8915c4893ad3f551ff124bfae2d484ee98 100755 (executable)
@@ -229,7 +229,7 @@ shell_command_test "shell.bad-auth" \
     "Failed to run: HTTP Error 401: Unauthorized"
 shell_command_test "shell.authorized" \
     "--auth-user pet --auth-password meow" "list-commands" "" \
-    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
 shell_command_test "shell.bad-auth-password-file" \
     "--auth-user foo --auth-password-file foobar" "list-commands" "fail" \
     "Failed to run: [Errno 2] No such file or directory: 'foobar'"
@@ -238,7 +238,7 @@ shell_command_test "shell.bad-auth-password-file-content" \
     "Failed to run: HTTP Error 401: Unauthorized"
 shell_command_test "shell.good-auth-password-file-content" \
     "--auth-user pet --auth-password-file ${tmpfile_path}/auth_password_file" "list-commands" "" \
-    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
 shell_command_test "shell.flag-precedence" \
     "--auth-user pet --auth-password meow --auth-password-file ${tmpfile_path}/auth_bad_password_file" "list-commands" "fail" \
     "Failed to run: HTTP Error 401: Unauthorized"
index ba59ffa9d256c70cbbffa85a81a2bdca16cb54da..df7a816977b86b13a36b53a8c9f8dc64fc2d5146 100755 (executable)
@@ -234,7 +234,7 @@ shell_command_test "shell.bad-auth" \
     "Failed to run: HTTP Error 401: Unauthorized"
 shell_command_test "shell.authorized" \
     "--auth-user pet --auth-password meow" "list-commands" "" \
-    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
 shell_command_test "shell.bad-auth-password-file" \
     "--auth-user foo --auth-password-file foobar" "list-commands" "fail" \
     "Failed to run: [Errno 2] No such file or directory: 'foobar'"
@@ -243,7 +243,7 @@ shell_command_test "shell.bad-auth-password-file-content" \
     "Failed to run: HTTP Error 401: Unauthorized"
 shell_command_test "shell.good-auth-password-file-content" \
     "--auth-user pet --auth-password-file ${tmpfile_path}/auth_password_file" "list-commands" "" \
-    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
 shell_command_test "shell.flag-precedence" \
     "--auth-user pet --auth-password meow --auth-password-file ${tmpfile_path}/auth_bad_password_file" "list-commands" "fail" \
     "Failed to run: HTTP Error 401: Unauthorized"
index 243670b62f9b3bcc4875c8263101d95fc1c2a4d0..3bc1968968e5c1cfdc35e26e2320ad4681cc03ac 100755 (executable)
@@ -195,7 +195,7 @@ shell_command_test() {
 }
 
 shell_command_test "shell.list-commands" "list-commands" \
-    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]" ""
+    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]" ""
 shell_command_test "shell.bogus" "give-me-a-beer" \
 "[ { \"result\": 2, \"text\": \"'give-me-a-beer' command not supported.\" } ]" ""
 shell_command_test "shell.empty-config-test" "config-test" \
index 7948e4ebe646b3af60cfd3bc70fd0921296855bc..12d672e12758b8476cb0d14eaa09570f14a354a7 100755 (executable)
@@ -200,7 +200,7 @@ shell_command_test() {
 }
 
 shell_command_test "shell.list-commands" "list-commands" \
-    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]" ""
+    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]" ""
 shell_command_test "shell.bogus" "give-me-a-beer" \
 "[ { \"result\": 2, \"text\": \"'give-me-a-beer' command not supported.\" } ]" ""
 shell_command_test "shell.empty-config-test" "config-test" \
index 2147acadf62a045acac20e4e9ebcb78a71f658b9..882020e751888a172140334b388e216c4ae24db3 100755 (executable)
@@ -306,10 +306,10 @@ list_commands_test() {
 }
 
 list_commands_test "NoTLS" "${CONFIG_NONE}" "" \
-"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
 list_commands_test "Encrypted" "${CONFIG_NOCR}" \
 "--ca ${TEST_CA_DIR}/kea-ca.crt" \
-"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
 list_commands_test "Authenticated" "${CONFIG}" \
 "--ca ${TEST_CA_DIR}/kea-ca.crt --cert ${TEST_CA_DIR}/kea-client.crt --key ${TEST_CA_DIR}/kea-client.key" \
-"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
index a72779d35437fad1a90989548f662de6c686ab97..0c194c279ae854ab29ba0846d9b0703532f26090 100755 (executable)
@@ -321,10 +321,10 @@ list_commands_test() {
 }
 
 list_commands_test "NoTLS" "${CONFIG_NONE}" "" \
-"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
 list_commands_test "Encrypted" "${CONFIG_NOCR}" \
 "--ca ${TEST_CA_DIR}/kea-ca.crt" \
-"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
 list_commands_test "Authenticated" "${CONFIG}" \
 "--ca ${TEST_CA_DIR}/kea-ca.crt --cert ${TEST_CA_DIR}/kea-client.crt --key ${TEST_CA_DIR}/kea-client.key" \
-"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
+"[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"interface-list\", \"interface-redetect\", \"interface-use\", \"kea-lfc-start\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-global-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet6-select-test\", \"version-get\" ], \"result\": 0 } ]"
index 58f82b516f04d6a3a7b09990b69ac9ffe7f378bc..d3cfbc647790e24c95c737a1ffb50d869238a0b5 100644 (file)
@@ -8,6 +8,7 @@
 #include <asiolink/asio_wrapper.h>
 #include <asiolink/io_error.h>
 #include <asiolink/udp_endpoint.h>
+#include <cc/data.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp_log.h>
@@ -37,6 +38,7 @@
 
 using namespace std;
 using namespace isc::asiolink;
+using namespace isc::data;
 using namespace isc::util;
 using namespace isc::util::io;
 using namespace isc::util::io::internal;
@@ -284,6 +286,28 @@ Iface::countActive4() const {
     return (count);
 }
 
+ElementPtr
+Iface::toElement() const {
+    ElementPtr result = Element::createMap();
+    result->set("name", Element::create(name_));
+    result->set("index", Element::create(ifindex_));
+    result->set("mac", Element::create(getPlainMac()));
+    result->set("type", Element::create(hardware_type_));
+    result->set("flag-loopback", Element::create(flag_loopback_));
+    result->set("flag-up", Element::create(flag_up_));
+    result->set("flag-running", Element::create(flag_running_));
+    result->set("flag-multicast", Element::create(flag_multicast_));
+    result->set("flag-broadcast", Element::create(flag_broadcast_));
+    bool in_use = IfaceMgr::instance().getFamily() == AF_INET ? !inactive4_ : !inactive6_;
+    result->set("in-use", Element::create(in_use));
+    ElementPtr addrs = Element::createList();
+    for (auto const& addr : addrs_) {
+        addrs->add(Element::create(addr.get().toText()));
+    }
+    result->set("addresses", addrs);
+    return (result);
+}
+
 void IfaceMgr::closeSockets() {
     // Clears bound addresses.
     clearBoundAddresses();
@@ -316,6 +340,15 @@ IfaceMgr::~IfaceMgr() {
     closeSockets();
 }
 
+ElementPtr
+IfaceMgr::ifacesToElement() const {
+    ElementPtr result = Element::createList();
+    for (auto const& iface: ifaces_) {
+        result->add(iface->toElement());
+    }
+    return (result);
+}
+
 bool
 IfaceMgr::isDirectResponseSupported() const {
     return (packet_filter_->isDirectResponseSupported());
index 111461f85b4984cfe033cf5094778705f000e5c4..bea1f871b88148d77f8a459a0f35f25dc620f654 100644 (file)
@@ -133,7 +133,7 @@ public:
 /// In order to avoid potentially expensive copies of the @c Iface objects
 /// holding pre-allocated buffers and multiple containers, this class is
 /// noncopyable.
-class Iface : public boost::noncopyable {
+class Iface : public isc::data::CfgToElement, public boost::noncopyable {
 public:
 
     /// Maximum MAC address length (Infiniband uses 20 bytes)
@@ -465,6 +465,17 @@ public:
     /// @return the list of messages
     ErrorBuffer const& getErrors() const;
 
+    /// @brief Unparse a configuration object
+    ///
+    /// Returns an element which must parse into the same object, i.e.
+    /// @code
+    /// for all valid config C parse(parse(C)->toElement()) == parse(C)
+    /// @endcode
+    ///
+    /// @return a pointer to a configuration which can be parsed into
+    /// the initial configuration object
+    virtual isc::data::ElementPtr toElement() const;
+
 protected:
     /// Socket used to send data.
     SocketCollection sockets_;
@@ -1492,6 +1503,21 @@ public:
     bool configureDHCPPacketQueue(const uint16_t family,
                                   data::ConstElementPtr queue_control);
 
+    /// @brief Sets address family (AF_INET or AF_INET6)
+    void setFamily(uint16_t family) {
+        family_ = family == AF_INET ? AF_INET : AF_INET6;
+    }
+
+    /// @brief Returns address family.
+    uint16_t getFamily() const {
+        return (family_);
+    }
+
+    /// @brief Unparses detected interface list.
+    ///
+    /// @return A pointer to unparsed detected interface list.
+    isc::data::ElementPtr ifacesToElement() const;
+
     // don't use private, we need derived classes in tests
 protected:
 
@@ -1817,6 +1843,9 @@ private:
 
     /// @brief The receiver FDEventHandler instance.
     util::FDEventHandlerPtr receiver_fd_event_handler_;
+
+    /// @brief Address family.
+    uint16_t family_;
 };
 
 }  // namespace isc::dhcp
index 48cd3a88aa16c84b108031741dc139941ada661b..67125503c56c5c1baf0d8858e818d8cc7313aa34 100644 (file)
@@ -95,6 +95,16 @@ CfgIface::openSockets(const uint16_t family, const uint16_t port,
     }
 }
 
+void
+CfgIface::triggerOpenSocketsWithRetry(const uint16_t family, const uint16_t port,
+                                      const bool use_bcast) {
+    // Use broadcast only if we're using raw sockets. For the UDP sockets,
+    // we only handle the relayed (unicast) traffic.
+    const bool can_use_bcast = use_bcast && (socket_type_ == SOCKET_RAW);
+
+    openSocketsWithRetry(reconnect_ctl_, family, port, can_use_bcast);
+}
+
 std::pair<bool, bool>
 CfgIface::openSocketsForFamily(const uint16_t family, const uint16_t port,
                                const bool can_use_bcast, const bool skip_opened) {
@@ -536,13 +546,70 @@ CfgIface::use(const uint16_t family, const std::string& iface_name) {
 
         // Log that we're listening on the specific interface and that the
         // address is not explicitly specified.
-        if (addr_str.empty()) {
-            LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_ADD_IFACE).arg(name);
-        }
+        LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_ADD_IFACE).arg(name);
         iface_set_.insert(name);
     }
 }
 
+bool
+CfgIface::merge(const CfgIface& other, const uint16_t family) {
+    bool updated = false;
+    if (other.wildcard_used_) {
+        wildcard_used_ = true;
+        updated = true;
+    } else {
+        // Use a copy of the containers so that no change is applied until everything is checked.
+        auto address_map = address_map_;
+        auto iface_set = iface_set_;
+        for (auto const& addr_key : other.address_map_) {
+            // For the IPv4, if the interface name was specified (instead of the interface-
+            // address tuple) all addresses are already activated. Adding an explicit address
+            // for the interface should result in error.
+            if ((family == AF_INET) && (iface_set.find(addr_key.first) != iface_set.end())) {
+                isc_throw(DuplicateIfaceName, "interface '" << addr_key.first
+                          << "' has already been selected");
+            }
+            // Check if the address hasn't been selected already.
+            std::pair<const std::string, IOAddress> iface_address_tuple(addr_key.first, addr_key.second);
+            if (std::find(address_map.begin(), address_map.end(),
+                          iface_address_tuple) != address_map.end()) {
+                isc_throw(DuplicateAddress, "must not select address '"
+                          << addr_key.second << "' for interface '" << addr_key.first << "' "
+                          "because this address is already selected");
+            }
+            address_map.insert(addr_key);
+        }
+        for (auto const& name : other.iface_set_) {
+            if ((name != ALL_IFACES_KEYWORD)) {
+                // An interface has been selected or an IPv4 address on this interface
+                // has been selected it is not allowed to select the whole interface.
+                if ((iface_set.find(name) != iface_set.end()) ||
+                    ((family == AF_INET) && address_map.count(name) > 0)) {
+                    isc_throw(DuplicateIfaceName, "interface '" << name
+                              << "' has already been specified");
+                }
+                iface_set.insert(name);
+            }
+        }
+        // Ready to apply the changes.
+        for (auto const& addr_key : other.address_map_) {
+            address_map_.insert(addr_key);
+            updated = true;
+        }
+        for (auto const& name : other.iface_set_) {
+            if ((name != ALL_IFACES_KEYWORD)) {
+                // Log that we're listening on the specific interface and that the
+                // address is not explicitly specified.
+                LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_ADD_IFACE).arg(name);
+                iface_set_.insert(name);
+                updated = true;
+            }
+        }
+    }
+
+    return (updated);
+}
+
 void
 CfgIface::useSocketType(const uint16_t family,
                         const SocketType& socket_type) {
index dd14fa99b2d6a47d066e74f53d2de45f411975d9..c94aa1e38d011698359a89002de0846bbe2d3277 100644 (file)
@@ -207,6 +207,14 @@ public:
     /// @c CfgIface::use has been already called for this interface.
     void use(const uint16_t family, const std::string& iface_name);
 
+    /// @brief Merge the interface list and address list.
+    ///
+    /// @param other The object from which the lists are updated.
+    /// @param family Address family (AF_INET or AF_INET6).
+    ///
+    /// @return true if any change has been done, false otherwise.
+    bool merge(const CfgIface& other, const uint16_t family);
+
     /// @brief Sets the specified socket type to be used by the server.
     ///
     /// Supported socket types for DHCPv4 are:
@@ -360,6 +368,17 @@ public:
     /// opening sockets fail.
     static OpenSocketsFailedCallback open_sockets_failed_callback_;
 
+    /// @brief Trigger the reopen sockets with retry mechanism.
+    ///
+    /// @param family Address family (AF_INET or AF_INET6).
+    /// @param port Port number to be used to bind sockets to.
+    /// @param use_bcast A boolean flag which indicates if the broadcast
+    /// traffic should be received through the socket and the raw sockets are
+    /// used. For the UDP sockets, we only handle the relayed (unicast)
+    /// traffic. This parameter is ignored for IPv6.
+    void triggerOpenSocketsWithRetry(const uint16_t family, const uint16_t port,
+                                     const bool use_bcast = true);
+
 private:
 
     /// @brief Checks if multiple IPv4 addresses has been activated on any
index b7d49277f42ad0543f629d616effcc676a6b244b..3a926abf05e3fa5f143d2162877132280b40f0fd 100644 (file)
@@ -22,9 +22,9 @@
         "            \"index\": 4,",
         "            \"mac\": \"00:11:22:33:44:55\",",
         "            \"type\": 6,",
-        "            \"addresses\" [ \"192.168.1.1\", \"10.10.1.1\",", 
-        "                            \"2003:db8:1::1\", \"3001:db8:1::1\",",
-        "                            \"fe80::3a60:77ff:fed5:abcd\" ],",
+        "            \"addresses\": [ \"192.168.1.1\", \"10.10.1.1\",",
+        "                             \"2003:db8:1::1\", \"3001:db8:1::1\",",
+        "                             \"fe80::3a60:77ff:fed5:abcd\" ],",
         "            \"flag-loopback\": false,",
         "            \"flag-up\": true,",
         "            \"flag-running\": true,",
@@ -36,9 +36,9 @@
         "            \"index\": 7,",
         "            \"mac\": \"11:22:33:44:55:66\",",
         "            \"type\": 6,",
-        "            \"addresses\" [ \"10.10.1.2\",", 
-        "                            \"2003:db8:1::2\",",
-        "                            \"fe80::3a60:77ff:fed5:1234\" ],",
+        "            \"addresses\": [ \"10.10.1.2\",",
+        "                             \"2003:db8:1::2\",",
+        "                             \"fe80::3a60:77ff:fed5:1234\" ],",
         "            \"flag-loopback\": false,",
         "            \"flag-up\": true,",
         "            \"flag-running\": true,",
index 38b68b9d306bcc8abef76c0e1c0358268d30d3ec..7dcb8b10e676fa9086fbb80b2976d356c16e6c6c 100644 (file)
@@ -23,9 +23,9 @@
         "            \"index\": 4,",
         "            \"mac\": \"00:11:22:33:44:55\",",
         "            \"type\": 6,",
-        "            \"addresses\" [ \"192.168.1.1\", \"10.10.1.1\", \"130.50.1.1\",", 
-        "                            \"2003:db8:1::1\", \"3001:db8:1::1\",",
-        "                            \"fe80::3a60:77ff:fed5:abcd\" ],",
+        "            \"addresses\": [ \"192.168.1.1\", \"10.10.1.1\", \"130.50.1.1\",",
+        "                             \"2003:db8:1::1\", \"3001:db8:1::1\",",
+        "                             \"fe80::3a60:77ff:fed5:abcd\" ],",
         "            \"flag-loopback\": false,",
         "            \"flag-up\": true,",
         "            \"flag-running\": true,",
@@ -37,9 +37,9 @@
         "            \"index\": 7,",
         "            \"mac\": \"11:22:33:44:55:66\",",
         "            \"type\": 6,",
-        "            \"addresses\" [ \"10.10.1.2\",", 
-        "                            \"2003:db8:1::2\",",
-        "                            \"fe80::3a60:77ff:fed5:1234\" ],",
+        "            \"addresses\": [ \"10.10.1.2\",",
+        "                             \"2003:db8:1::2\",",
+        "                             \"fe80::3a60:77ff:fed5:1234\" ],",
         "            \"flag-loopback\": false,",
         "            \"flag-up\": true,",
         "            \"flag-running\": true,",
@@ -51,9 +51,9 @@
         "            \"index\": 8,",
         "            \"mac\": \"22:33:44:55:66:77\",",
         "            \"type\": 6,",
-        "            \"addresses\" [ \"192.168.1.2\",", 
-        "                            \"3001:db8:1::2\",",
-        "                            \"fe80::3a60:77ff:fed5:5678\" ],",
+        "            \"addresses\": [ \"192.168.1.2\",",
+        "                             \"3001:db8:1::2\",",
+        "                             \"fe80::3a60:77ff:fed5:5678\" ],",
         "            \"flag-loopback\": false,",
         "            \"flag-up\": true,",
         "            \"flag-running\": false,",
index 0630c34f6e4341b55b6f377b3abacd0c4ffa9183..b252688824488eca9489fc8207a3f73aacf7917f 100644 (file)
@@ -22,7 +22,6 @@
         "{",
         "    \"result\": 0,",
         "    \"text\": \"Operation succeeded.\"",
-        "    }",
         "}"
     ],
     "support": [