]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3144] added unit tests
authorRazvan Becheriu <razvan@isc.org>
Wed, 1 Apr 2026 21:44:03 +0000 (00:44 +0300)
committerRazvan Becheriu <razvan@isc.org>
Mon, 18 May 2026 11:22:02 +0000 (14:22 +0300)
doc/sphinx/arm/ctrl-channel.rst
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/http_control_socket_unittest.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/http_control_socket_unittest.cc
src/lib/dhcp/iface_mgr.cc
src/lib/dhcp/tests/iface_mgr_unittest.cc
src/lib/dhcpsrv/tests/cfg_iface_unittest.cc
src/share/api/interface-list.json
src/share/api/interface-redetect.json
src/share/api/interface-use.json

index a5c3eb80fb4c333af375568408cac737c07e70ef..679285f7b04513520be7b92df6ecc8ad1a52d38c 100644 (file)
@@ -490,7 +490,8 @@ information about the processing of expired leases (lease reclamation).
 The ``interface-list`` Command
 ------------------------------
 
-The :isccmd:`interface-list` command retrieves.
+The :isccmd:`interface-list` command retrieves the list of detected interfaces.
+This command does not take any parameters.
 
 .. isccmd:: interface-redetect
 .. _command-interface-redetect:
@@ -498,7 +499,9 @@ The :isccmd:`interface-list` command retrieves.
 The ``interface-redetect`` Command
 ----------------------------------
 
-The :isccmd:`interface-redetect` command retrieves.
+The :isccmd:`interface-redetect` command retrieves the list of detected interfaces
+after performing a re-detect procedure.
+This command does not take any parameters."
 
 .. isccmd:: interface-use
 .. _command-interface-use:
@@ -506,7 +509,22 @@ The :isccmd:`interface-redetect` command retrieves.
 The ``interface-use`` Command
 -----------------------------
 
-The :isccmd:`interface-use` command updates.
+The :isccmd:`interface-use` command updates the list of interfaces used
+to process DHCP traffic.
+The command takes as parameter the list of interfaces with respective
+addresses (if specified) on which the server should start listening for
+DHCP traffic.
+
+Please note that the new configuration is
+retained in memory only; if the server is restarted or a configuration
+reload is triggered via a signal, the server uses the configuration
+stored in its configuration file. The server's response contains a
+numeric code, ``result`` (0 for success, non-zero on failure), and a
+string, ``text``, describing the outcome:
+
+::
+
+       {"result": 0, "text": "Configuration successful." }
 
 .. isccmd:: list-commands
 .. _command-list-commands:
index 156d24835252cb2594de033304c7814a352ded8a..f4ee1317a69cd82bcd6741084930b88a0f6fae38 100644 (file)
@@ -131,6 +131,7 @@ public:
         resetLogPath();
         setSocketTestPath();
         reset();
+        IfaceMgr::instance().setFamily(AF_INET);
         IfaceMgr::instance().setTestMode(false);
         IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
                                                IfaceMgr::instancePtr().get(), ph::_1));
@@ -549,6 +550,9 @@ TEST_F(CtrlChannelDhcpv4SrvTest, commandsRegistration) {
     EXPECT_TRUE(command_list.find("\"config-hash-get\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"interface-list\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"interface-redetect\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"interface-use\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"kea-lfc-start\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"subnet4-select-test\"") != string::npos);
@@ -1870,6 +1874,9 @@ TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) {
     checkListCommands(rsp, "config-set");
     checkListCommands(rsp, "config-test");
     checkListCommands(rsp, "config-write");
+    checkListCommands(rsp, "interface-list");
+    checkListCommands(rsp, "interface-redetect");
+    checkListCommands(rsp, "interface-use");
     checkListCommands(rsp, "kea-lfc-start");
     checkListCommands(rsp, "list-commands");
     checkListCommands(rsp, "leases-reclaim");
@@ -2421,6 +2428,173 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSetDetectInterfaces) {
     EXPECT_EQ(2U, subnets->size());
 }
 
+// Tests if interface-list works properly.
+TEST_F(CtrlChannelDhcpv4SrvTest, interfaceList) {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createUnixChannelServer();
+    SKIP_IF(skipped_);
+    std::string response;
+
+    std::string command = "{ \"command\": \"interface-list\" }";
+
+    sendUnixCommand(command, response);
+    string expected = "{ \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"1 interfaces detected.\" }";
+    EXPECT_EQ(response, expected);
+}
+
+// Tests if interface-redetect works properly.
+TEST_F(CtrlChannelDhcpv4SrvTest, interfaceRedetect) {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    eth0->inactive4_ = true;
+    eth0->inactive6_ = true;
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createUnixChannelServer();
+    SKIP_IF(skipped_);
+    std::string response;
+
+    IfacePtr eth1 = IfaceMgrTestConfig::createIface("eth1", ETH1_INDEX,
+                                                    "AA:BB:CC:DD:EE:FF");
+    eth1->inactive4_ = true;
+    eth1->inactive6_ = true;
+    auto detectUpdateIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+            eth1->addAddress(IOAddress("192.0.2.3"));
+            eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+            eth1->addAddress(IOAddress("3001:db8:100::1"));
+            IfaceMgr::instance().addInterface(eth1);
+        } else {
+            if (!IfaceMgr::instance().getIface("eth1")) {
+                eth1->addAddress(IOAddress("192.0.2.3"));
+                eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+                eth1->addAddress(IOAddress("3001:db8:100::1"));
+                IfaceMgr::instance().addInterface(eth1);
+            }
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectUpdateIfaces);
+
+    std::string command = "{ \"command\": \"interface-redetect\" }";
+
+    sendUnixCommand(command, response);
+    string expected = "{ \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "}, { "
+            "\"addresses\": [ \"192.0.2.3\", \"fe80::3a60:77ff:fed5:abcd\", \"3001:db8:100::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 2, "
+            "\"mac\": \"aa:bb:cc:dd:ee:ff\", "
+            "\"name\": \"eth1\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"2 interfaces detected.\" }";
+    EXPECT_EQ(response, expected);
+}
+
+// Tests if interface-use works properly.
+TEST_F(CtrlChannelDhcpv4SrvTest, interfaceUse) {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createUnixChannelServer();
+    SKIP_IF(skipped_);
+    std::string response;
+
+    std::string command = "{ \"command\": \"interface-use\", \"arguments\": { \"interfaces\": [ \"eth0\" ] } }";
+
+    sendUnixCommand(command, response);
+    EXPECT_EQ(response, "{ \"result\": 0, \"text\": \"Configuration successful.\" }");
+
+    command = "{ \"command\": \"interface-list\" }";
+    sendUnixCommand(command, response);
+    string expected = "{ \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": true, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"1 interfaces detected.\" }";
+    EXPECT_EQ(response, expected);
+}
+
 // This test verifies that disable DHCP service command performs sanity check on
 // parameters.
 TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisableBadParam) {
index 62afb7e4d4230616e78b0ac2fb1407c0794cbd01..186c9789b69efaeb1e9bcd6fe053984677d904da 100644 (file)
@@ -106,6 +106,7 @@ public:
     /// Sets socket path to its default value.
     BaseCtrlChannelDhcpv4Test() : interfaces_("\"*\""), handle_stop_(false) {
         reset();
+        IfaceMgr::instance().setFamily(AF_INET);
         IfaceMgr::instance().setTestMode(false);
         IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
                                                IfaceMgr::instancePtr().get(), ph::_1));
@@ -573,6 +574,15 @@ public:
     // which is added after server startup.
     void testConfigSetDetectInterfaces();
 
+    // Tests if interface-list works properly.
+    void testInterfaceList();
+
+    // Tests if interface-redetect works properly.
+    void testInterfaceRedetect();
+
+    // Tests if interface-use works properly.
+    void testInterfaceUse();
+
     // This test verifies that disable DHCP service command performs
     // sanity check on parameters.
     void testDhcpDisableBadParam();
@@ -2969,6 +2979,9 @@ BaseCtrlChannelDhcpv4Test::testListCommands() {
     checkListCommands(rsp, "config-set");
     checkListCommands(rsp, "config-test");
     checkListCommands(rsp, "config-write");
+    checkListCommands(rsp, "interface-list");
+    checkListCommands(rsp, "interface-redetect");
+    checkListCommands(rsp, "interface-use");
     checkListCommands(rsp, "kea-lfc-start");
     checkListCommands(rsp, "list-commands");
     checkListCommands(rsp, "leases-reclaim");
@@ -3624,6 +3637,197 @@ TEST_F(HttpsCtrlChannelDhcpv4Test, configSetDetectInterfaces) {
     testConfigSetDetectInterfaces();
 }
 
+// Tests if interface-list works properly.
+void
+BaseCtrlChannelDhcpv4Test::testInterfaceList() {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createHttpChannelServer();
+    std::string response;
+
+    std::string command = "{ \"command\": \"interface-list\" }";
+
+    sendHttpCommand(command, response);
+    string expected = "[ { \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"1 interfaces detected.\" } ]";
+    EXPECT_EQ(response, expected);
+}
+
+TEST_F(HttpCtrlChannelDhcpv4Test, interfaceList) {
+    testInterfaceList();
+}
+
+TEST_F(HttpsCtrlChannelDhcpv4Test, interfaceList) {
+    testInterfaceList();
+}
+
+// Tests if interface-redetect works properly.
+void
+BaseCtrlChannelDhcpv4Test::testInterfaceRedetect() {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    eth0->inactive4_ = true;
+    eth0->inactive6_ = true;
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createHttpChannelServer();
+    std::string response;
+
+    IfacePtr eth1 = IfaceMgrTestConfig::createIface("eth1", ETH1_INDEX,
+                                                    "AA:BB:CC:DD:EE:FF");
+    eth1->inactive4_ = true;
+    eth1->inactive6_ = true;
+    auto detectUpdateIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+            eth1->addAddress(IOAddress("192.0.2.3"));
+            eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+            eth1->addAddress(IOAddress("3001:db8:100::1"));
+            IfaceMgr::instance().addInterface(eth1);
+        } else {
+            if (!IfaceMgr::instance().getIface("eth1")) {
+                eth1->addAddress(IOAddress("192.0.2.3"));
+                eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+                eth1->addAddress(IOAddress("3001:db8:100::1"));
+                IfaceMgr::instance().addInterface(eth1);
+            }
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectUpdateIfaces);
+
+    std::string command = "{ \"command\": \"interface-redetect\" }";
+
+    sendHttpCommand(command, response);
+    string expected = "[ { \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "}, { "
+            "\"addresses\": [ \"192.0.2.3\", \"fe80::3a60:77ff:fed5:abcd\", \"3001:db8:100::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 2, "
+            "\"mac\": \"aa:bb:cc:dd:ee:ff\", "
+            "\"name\": \"eth1\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"2 interfaces detected.\" } ]";
+    EXPECT_EQ(response, expected);
+}
+
+TEST_F(HttpCtrlChannelDhcpv4Test, interfaceRedetect) {
+    testInterfaceRedetect();
+}
+
+TEST_F(HttpsCtrlChannelDhcpv4Test, interfaceRedetect) {
+    testInterfaceRedetect();
+}
+
+// Tests if interface-use works properly.
+void
+BaseCtrlChannelDhcpv4Test::testInterfaceUse() {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createHttpChannelServer();
+    std::string response;
+
+    std::string command = "{ \"command\": \"interface-use\", \"arguments\": { \"interfaces\": [ \"eth0\" ] } }";
+
+    sendHttpCommand(command, response);
+    EXPECT_EQ(response, "[ { \"result\": 0, \"text\": \"Configuration successful.\" } ]");
+
+    command = "{ \"command\": \"interface-list\" }";
+    sendHttpCommand(command, response);
+    string expected = "[ { \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": true, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"1 interfaces detected.\" } ]";
+    EXPECT_EQ(response, expected);
+}
+
+TEST_F(HttpCtrlChannelDhcpv4Test, interfaceUse) {
+    testInterfaceUse();
+}
+
+TEST_F(HttpsCtrlChannelDhcpv4Test, interfaceUse) {
+    testInterfaceUse();
+}
+
 // This test verifies that disable DHCP service command performs
 // sanity check on parameters.
 void
index bcf60651aaffa99117ac68d1107d24b7568ed263..662a8947f0db7395b223c70e8364cdfeeb2e4b97 100644 (file)
@@ -162,6 +162,7 @@ public:
     /// Sets socket path to its default value.
     CtrlChannelDhcpv6SrvTest() : interfaces_("\"*\""), skipped_(false) {
         reset();
+        IfaceMgr::instance().setFamily(AF_INET6);
         IfaceMgr::instance().setTestMode(false);
         IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
                                                IfaceMgr::instancePtr().get(), ph::_1));
@@ -552,6 +553,9 @@ TEST_F(CtrlDhcpv6SrvTest, commandsRegistration) {
     EXPECT_TRUE(command_list.find("\"config-hash-get\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"interface-list\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"interface-redetect\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"interface-use\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"kea-lfc-start\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"subnet6-select-test\"") != string::npos);
@@ -1870,6 +1874,9 @@ TEST_F(CtrlChannelDhcpv6SrvTest, listCommands) {
     checkListCommands(rsp, "config-set");
     checkListCommands(rsp, "config-test");
     checkListCommands(rsp, "config-write");
+    checkListCommands(rsp, "interface-list");
+    checkListCommands(rsp, "interface-redetect");
+    checkListCommands(rsp, "interface-use");
     checkListCommands(rsp, "kea-lfc-start");
     checkListCommands(rsp, "list-commands");
     checkListCommands(rsp, "leases-reclaim");
@@ -2415,6 +2422,173 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configSetDetectInterfaces) {
     EXPECT_EQ(2U, subnets->size());
 }
 
+// Tests if interface-list works properly.
+TEST_F(CtrlChannelDhcpv6SrvTest, interfaceList) {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createUnixChannelServer();
+    SKIP_IF(skipped_);
+    std::string response;
+
+    std::string command = "{ \"command\": \"interface-list\" }";
+
+    sendUnixCommand(command, response);
+    string expected = "{ \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"1 interfaces detected.\" }";
+    EXPECT_EQ(response, expected);
+}
+
+// Tests if interface-redetect works properly.
+TEST_F(CtrlChannelDhcpv6SrvTest, interfaceRedetect) {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    eth0->inactive4_ = true;
+    eth0->inactive6_ = true;
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createUnixChannelServer();
+    SKIP_IF(skipped_);
+    std::string response;
+
+    IfacePtr eth1 = IfaceMgrTestConfig::createIface("eth1", ETH1_INDEX,
+                                                    "AA:BB:CC:DD:EE:FF");
+    eth1->inactive4_ = true;
+    eth1->inactive6_ = true;
+    auto detectUpdateIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+            eth1->addAddress(IOAddress("192.0.2.3"));
+            eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+            eth1->addAddress(IOAddress("3001:db8:100::1"));
+            IfaceMgr::instance().addInterface(eth1);
+        } else {
+            if (!IfaceMgr::instance().getIface("eth1")) {
+                eth1->addAddress(IOAddress("192.0.2.3"));
+                eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+                eth1->addAddress(IOAddress("3001:db8:100::1"));
+                IfaceMgr::instance().addInterface(eth1);
+            }
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectUpdateIfaces);
+
+    std::string command = "{ \"command\": \"interface-redetect\" }";
+
+    sendUnixCommand(command, response);
+    string expected = "{ \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "}, { "
+            "\"addresses\": [ \"192.0.2.3\", \"fe80::3a60:77ff:fed5:abcd\", \"3001:db8:100::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 2, "
+            "\"mac\": \"aa:bb:cc:dd:ee:ff\", "
+            "\"name\": \"eth1\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"2 interfaces detected.\" }";
+    EXPECT_EQ(response, expected);
+}
+
+// Tests if interface-use works properly.
+TEST_F(CtrlChannelDhcpv6SrvTest, interfaceUse) {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createUnixChannelServer();
+    SKIP_IF(skipped_);
+    std::string response;
+
+    std::string command = "{ \"command\": \"interface-use\", \"arguments\": { \"interfaces\": [ \"eth0\" ] } }";
+
+    sendUnixCommand(command, response);
+    EXPECT_EQ(response, "{ \"result\": 0, \"text\": \"Configuration successful.\" }");
+
+    command = "{ \"command\": \"interface-list\" }";
+    sendUnixCommand(command, response);
+    string expected = "{ \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": true, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"1 interfaces detected.\" }";
+    EXPECT_EQ(response, expected);
+}
+
 // This test verifies that disable DHCP service command performs sanity check on
 // parameters.
 TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisableBadParam) {
index e33cdc58b1f6cad3b35156cfc440eb4b4af80d00..6b54f16109e207464dff4becb5940da5639d96f7 100644 (file)
@@ -142,6 +142,7 @@ public:
     /// Sets socket path to its default value.
     BaseCtrlChannelDhcpv6Test() : interfaces_("\"*\""), handle_stop_(false) {
         reset();
+        IfaceMgr::instance().setFamily(AF_INET6);
         IfaceMgr::instance().setTestMode(false);
         IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
                                                IfaceMgr::instancePtr().get(), ph::_1));
@@ -582,6 +583,15 @@ public:
     // which is added after server startup.
     void testConfigSetDetectInterfaces();
 
+    // Tests if interface-list works properly.
+    void testInterfaceList();
+
+    // Tests if interface-redetect works properly.
+    void testInterfaceRedetect();
+
+    // Tests if interface-use works properly.
+    void testInterfaceUse();
+
     // This test verifies that disable DHCP service command performs
     // sanity check on parameters.
     void testDhcpDisableBadParam();
@@ -2967,6 +2977,10 @@ BaseCtrlChannelDhcpv6Test::testListCommands() {
     checkListCommands(rsp, "config-set");
     checkListCommands(rsp, "config-test");
     checkListCommands(rsp, "config-write");
+    checkListCommands(rsp, "interface-list");
+    checkListCommands(rsp, "interface-redetect");
+    checkListCommands(rsp, "interface-use");
+    checkListCommands(rsp, "kea-lfc-start");
     checkListCommands(rsp, "list-commands");
     checkListCommands(rsp, "leases-reclaim");
     checkListCommands(rsp, "version-get");
@@ -3615,6 +3629,197 @@ TEST_F(HttpsCtrlChannelDhcpv6Test, configSetDetectInterfaces) {
     testConfigSetDetectInterfaces();
 }
 
+// Tests if interface-list works properly.
+void
+BaseCtrlChannelDhcpv6Test::testInterfaceList() {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createHttpChannelServer();
+    std::string response;
+
+    std::string command = "{ \"command\": \"interface-list\" }";
+
+    sendHttpCommand(command, response);
+    string expected = "[ { \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"1 interfaces detected.\" } ]";
+    EXPECT_EQ(response, expected);
+}
+
+TEST_F(HttpCtrlChannelDhcpv6Test, interfaceList) {
+    testInterfaceList();
+}
+
+TEST_F(HttpsCtrlChannelDhcpv6Test, interfaceList) {
+    testInterfaceList();
+}
+
+// Tests if interface-redetect works properly.
+void
+BaseCtrlChannelDhcpv6Test::testInterfaceRedetect() {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    eth0->inactive4_ = true;
+    eth0->inactive6_ = true;
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createHttpChannelServer();
+    std::string response;
+
+    IfacePtr eth1 = IfaceMgrTestConfig::createIface("eth1", ETH1_INDEX,
+                                                    "AA:BB:CC:DD:EE:FF");
+    eth1->inactive4_ = true;
+    eth1->inactive6_ = true;
+    auto detectUpdateIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+            eth1->addAddress(IOAddress("192.0.2.3"));
+            eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+            eth1->addAddress(IOAddress("3001:db8:100::1"));
+            IfaceMgr::instance().addInterface(eth1);
+        } else {
+            if (!IfaceMgr::instance().getIface("eth1")) {
+                eth1->addAddress(IOAddress("192.0.2.3"));
+                eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+                eth1->addAddress(IOAddress("3001:db8:100::1"));
+                IfaceMgr::instance().addInterface(eth1);
+            }
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectUpdateIfaces);
+
+    std::string command = "{ \"command\": \"interface-redetect\" }";
+
+    sendHttpCommand(command, response);
+    string expected = "[ { \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "}, { "
+            "\"addresses\": [ \"192.0.2.3\", \"fe80::3a60:77ff:fed5:abcd\", \"3001:db8:100::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 2, "
+            "\"mac\": \"aa:bb:cc:dd:ee:ff\", "
+            "\"name\": \"eth1\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"2 interfaces detected.\" } ]";
+    EXPECT_EQ(response, expected);
+}
+
+TEST_F(HttpCtrlChannelDhcpv6Test, interfaceRedetect) {
+    testInterfaceRedetect();
+}
+
+TEST_F(HttpsCtrlChannelDhcpv6Test, interfaceRedetect) {
+    testInterfaceRedetect();
+}
+
+// Tests if interface-use works properly.
+void
+BaseCtrlChannelDhcpv6Test::testInterfaceUse() {
+    interfaces_ = "";
+    IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+                                                    "11:22:33:44:55:66");
+    auto detectIfaces = [&](bool update_only) {
+        if (!update_only) {
+            eth0->addAddress(IOAddress("10.0.0.1"));
+            eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+            eth0->addAddress(IOAddress("2001:db8:1::1"));
+            IfaceMgr::instance().addInterface(eth0);
+        }
+        return (false);
+    };
+    IfaceMgr::instance().setDetectCallback(detectIfaces);
+    IfaceMgr::instance().clearIfaces();
+    IfaceMgr::instance().closeSockets();
+    IfaceMgr::instance().detectIfaces();
+    createHttpChannelServer();
+    std::string response;
+
+    std::string command = "{ \"command\": \"interface-use\", \"arguments\": { \"interfaces\": [ \"eth0\" ] } }";
+
+    sendHttpCommand(command, response);
+    EXPECT_EQ(response, "[ { \"result\": 0, \"text\": \"Configuration successful.\" } ]");
+
+    command = "{ \"command\": \"interface-list\" }";
+    sendHttpCommand(command, response);
+    string expected = "[ { \"arguments\": { \"interfaces\": [ { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": true, "
+            "\"index\": 1, "
+            "\"mac\": \"11:22:33:44:55:66\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 1 "
+            "} ] }, \"result\": 0, \"text\": \"1 interfaces detected.\" } ]";
+    EXPECT_EQ(response, expected);
+}
+
+TEST_F(HttpCtrlChannelDhcpv6Test, interfaceUse) {
+    testInterfaceUse();
+}
+
+TEST_F(HttpsCtrlChannelDhcpv6Test, interfaceUse) {
+    testInterfaceUse();
+}
+
 // This test verifies that disable DHCP service command performs
 // sanity check on parameters.
 void
index d3cfbc647790e24c95c737a1ffb50d869238a0b5..e3932ce523f4922adff9d3293bd9987cf133d08e 100644 (file)
@@ -180,7 +180,8 @@ bool Iface::delSocket(const uint16_t sockfd) {
 IfaceMgr::IfaceMgr()
     : packet_filter_(new PktFilterInet()),
       packet_filter6_(new PktFilterInet6()),
-      test_mode_(false), check_thread_id_(true), allow_loopback_(false) {
+      test_mode_(false), check_thread_id_(true),
+      allow_loopback_(false), family_(AF_INET) {
     id_ = std::this_thread::get_id();
 
     // Ensure that PQMs have been created to guarantee we have
index f61c7fee1f7b4bc57af88b3e236fd744532cdb98..96afc67c1aeb7f89afbbed0c6ce51e630eacf20f 100644 (file)
@@ -1890,6 +1890,55 @@ TEST_F(IfaceMgrTest, getIfaceByIndex) {
     EXPECT_TRUE(iface);
 }
 
+// This test checks the getIface by index method.
+TEST_F(IfaceMgrTest, interfacesToElement) {
+    NakedIfaceMgr ifacemgr;
+
+    // Create a set of fake interfaces. At the same time, remove the actual
+    // interfaces that have been detected by the IfaceMgr.
+    ifacemgr.createIfaces();
+
+    auto ifaces = ifacemgr.ifacesToElement();
+    std::string expected = "[ { "
+            "\"addresses\": [ \"127.0.0.1\", \"::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": true, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": false, "
+            "\"index\": 0, "
+            "\"mac\": \"\", "
+            "\"name\": \"lo\", "
+            "\"type\": 0 "
+            "}, { "
+            "\"addresses\": [ \"10.0.0.1\", \"fe80::3a60:77ff:fed5:cdef\", \"2001:db8:1::1\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": true, "
+            "\"index\": 1, "
+            "\"mac\": \"\", "
+            "\"name\": \"eth0\", "
+            "\"type\": 0 "
+            "}, { "
+            "\"addresses\": [ \"192.0.2.3\", \"fe80::3a60:77ff:fed5:abcd\" ], "
+            "\"flag-broadcast\": false, "
+            "\"flag-loopback\": false, "
+            "\"flag-multicast\": true, "
+            "\"flag-running\": true, "
+            "\"flag-up\": true, "
+            "\"in-use\": true, "
+            "\"index\": 2, "
+            "\"mac\": \"\", "
+            "\"name\": \"eth1\", "
+            "\"type\": 0 "
+            "} ]";
+    EXPECT_EQ(ifaces->str(), expected);
+}
+
 // This test checks the getIface by packet method.
 TEST_F(IfaceMgrTest, getIfaceByPkt) {
     NakedIfaceMgr ifacemgr;
@@ -3551,6 +3600,14 @@ TEST_F(IfaceMgrTest, hasOpenSocketForAddress6) {
     EXPECT_FALSE(ifacemgr.hasOpenSocket(IOAddress("fe80::3a60:77ff:feed:1")));
 }
 
+// Test the Iface structure itself
+TEST_F(IfaceMgrTest, family) {
+    NakedIfaceMgr ifacemgr;
+    EXPECT_EQ(ifacemgr.getFamily(), AF_INET);
+    ifacemgr.setFamily(AF_INET6);
+    EXPECT_EQ(ifacemgr.getFamily(), AF_INET6);
+}
+
 // Test the Iface structure itself
 TEST_F(IfaceMgrTest, iface) {
     boost::scoped_ptr<Iface> iface;
index c93566416529b09fd0b1b3e1c430e477dc1781fd..8fc0d70c7c66ae2a6e80cc1700efa1c3e3ea6447 100644 (file)
@@ -10,6 +10,7 @@
 #include <dhcp/testutils/pkt_filter_test_stub.h>
 #include <dhcp/testutils/pkt_filter6_test_stub.h>
 #include <dhcpsrv/cfg_iface.h>
+#include <dhcpsrv/parsers/ifaces_config_parser.h>
 #include <asiolink/io_service.h>
 #include <asiolink/asio_wrapper.h>
 #include <asiolink/interval_timer.h>
@@ -1737,6 +1738,44 @@ TEST_F(CfgIfaceTest, retryDoubleOpenServiceSockets6) {
     EXPECT_EQ(second_port_calls, 2);
 }
 
+// Test that merge CfgIface works properly.
+TEST_F(CfgIfaceTest, mergeV4CfgIface) {
+    CfgIfacePtr left(new CfgIface());
+    CfgIfacePtr right(new CfgIface());
+    std::string left_txt("{ \"interfaces\": [\"eth0\", \"eth1/192.0.2.3\"], \"re-detect\": false }");
+    std::string right_txt("{ \"interfaces\": [\"eth0/10.0.0.1\", \"eth1961\"], \"re-detect\": false }");
+    ConstElementPtr left_cfg = Element::fromJSON(left_txt);
+    ConstElementPtr right_cfg = Element::fromJSON(right_txt);
+    IfacesConfigParser parser(AF_INET, true);
+    EXPECT_NO_THROW(parser.parse(left, left_cfg));
+    auto lift_before = left->toElement()->str();
+    EXPECT_NO_THROW(parser.parse(right, right_cfg));
+    auto right_before = right->toElement()->str();
+    EXPECT_THROW(left->merge(*right, AF_INET), DuplicateIfaceName);
+    EXPECT_EQ(lift_before, left->toElement()->str());
+    EXPECT_THROW(right->merge(*left, AF_INET), DuplicateIfaceName);
+    EXPECT_EQ(right_before, right->toElement()->str());
+}
+
+// Test that merge CfgIface works properly.
+TEST_F(CfgIfaceTest, mergeV6CfgIface) {
+    CfgIfacePtr left(new CfgIface());
+    CfgIfacePtr right(new CfgIface());
+    std::string left_txt("{ \"interfaces\": [\"eth0/2001:db8:1::1\", \"eth1\"], \"re-detect\": false }");
+    std::string right_txt("{ \"interfaces\": [\"eth0/2001:db8:1::1\", \"eth1961\"], \"re-detect\": false }");
+    ConstElementPtr left_cfg = Element::fromJSON(left_txt);
+    ConstElementPtr right_cfg = Element::fromJSON(right_txt);
+    IfacesConfigParser parser(AF_INET6, true);
+    EXPECT_NO_THROW(parser.parse(left, left_cfg));
+    auto lift_before = left->toElement()->str();
+    EXPECT_NO_THROW(parser.parse(right, right_cfg));
+    auto right_before = right->toElement()->str();
+    EXPECT_THROW(left->merge(*right, AF_INET6), DuplicateAddress);
+    EXPECT_EQ(lift_before, left->toElement()->str());
+    EXPECT_THROW(right->merge(*left, AF_INET6), DuplicateAddress);
+    EXPECT_EQ(right_before, right->toElement()->str());
+}
+
 // This test verifies that it is possible to specify the socket
 // type to be used by the DHCPv4 server.
 // This test is enabled on LINUX and BSD only, because the
index 3a926abf05e3fa5f143d2162877132280b40f0fd..7f9ebfda5e885b4fe5517f53a8f6aab0ad6a957b 100644 (file)
@@ -2,7 +2,7 @@
     "access": "read",
     "avail": "3.1.8",
     "brief": [
-        "This command returns the list of detected interfaces.",
+        "This command retrieves the list of detected interfaces.",
         "This command does not take any parameters."
     ],
     "cmd-syntax": [
index 7dcb8b10e676fa9086fbb80b2976d356c16e6c6c..5f9e71cdfac08289b39090242ad2c61394290a19 100644 (file)
@@ -2,7 +2,7 @@
     "access": "write",
     "avail": "3.1.8",
     "brief": [
-        "This command returns the list of detected interfaces after performing",
+        "This command retrieves the list of detected interfaces after performing",
         "a re-detect procedure.",
         "This command does not take any parameters."
     ],
index b252688824488eca9489fc8207a3f73aacf7917f..163d5a70e996181f9ae4d7e5f6b350da280bdc12 100644 (file)
@@ -2,9 +2,8 @@
     "access": "write",
     "avail": "3.1.8",
     "brief": [
-        "This command returns the list of detected interfaces after performing",
-        "a re-detect procedure.",
-        "This command take as parameters the list of interfaces with respective",
+        "This command updates the list of interfaces used to process DHCP traffic.",
+        "This command takes as parameter the list of interfaces with respective",
         "addresses (if specified) on which the server should start listening for",
         "DHCP traffic."
     ],