- :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`
- :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`
#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>
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));
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));
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");
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
/// @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);
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) {
#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>
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));
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));
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");
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
/// @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);
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) {
"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'"
"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"
"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'"
"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"
}
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" \
}
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" \
}
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 } ]"
}
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 } ]"
#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>
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;
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();
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());
/// 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)
/// @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_;
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:
/// @brief The receiver FDEventHandler instance.
util::FDEventHandlerPtr receiver_fd_event_handler_;
+
+ /// @brief Address family.
+ uint16_t family_;
};
} // namespace isc::dhcp
}
}
+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) {
// 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) {
/// @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:
/// 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\": 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,",
" \"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\": 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,",
" \"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\": 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,",
"{",
" \"result\": 0,",
" \"text\": \"Operation succeeded.\"",
- " }",
"}"
],
"support": [