Browse Source

[3251] Implemented basic tests for IfaceMgr::openSockets6.

Marcin Siodelski 11 years ago
parent
commit
4276b45afa
2 changed files with 257 additions and 16 deletions
  1. 2 2
      src/lib/dhcp/iface_mgr.cc
  2. 255 14
      src/lib/dhcp/tests/iface_mgr_unittest.cc

+ 2 - 2
src/lib/dhcp/iface_mgr.cc

@@ -485,7 +485,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
                           << iface->getName() << ", reason: " << errstr);
             }
 
-            // Binding socket to unicast address and then joining multicast group
+            /*            // Binding socket to unicast address and then joining multicast group
             // works well on Mac OS (and possibly other BSDs), but does not work
             // on Linux.
             if ( !PktFilter6::joinMulticast(sock, iface->getName(),
@@ -494,7 +494,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
                 isc_throw(SocketConfigError, "Failed to join "
                           << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                           << " multicast group.");
-            }
+                          } */
 
             count++;
 

+ 255 - 14
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -36,6 +36,7 @@ using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using boost::scoped_ptr;
 
 namespace {
@@ -169,11 +170,21 @@ public:
         ifaces_.clear();
 
         // local loopback
-        ifaces_.push_back(createIface("lo", 0, "127.0.0.1"));
+        Iface lo = createIface("lo", 0);
+        lo.addAddress(IOAddress("127.0.0.1"));
+        lo.addAddress(IOAddress("::1"));
+        ifaces_.push_back(lo);
         // eth0
-        ifaces_.push_back(createIface("eth0", 1, "10.0.0.1"));
+        Iface eth0 = createIface("eth0", 1);
+        eth0.addAddress(IOAddress("10.0.0.1"));
+        eth0.addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+        eth0.addAddress(IOAddress("2001:db8:1::1"));
+        ifaces_.push_back(eth0);
         // eth1
-        ifaces_.push_back(createIface("eth1", 2, "192.0.2.3"));
+        Iface eth1 = createIface("eth1", 2);
+        eth1.addAddress(IOAddress("192.0.2.3"));
+        eth1.addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+        ifaces_.push_back(eth1);
     }
 
     /// @brief Create an object representing interface.
@@ -190,13 +201,10 @@ public:
     ///
     /// @param name A name of the interface to be created.
     /// @param ifindex An index of the interface to be created.
-    /// @param addr An IP address to be assigned to the interface.
     ///
     /// @return An object representing interface.
-    static Iface createIface(const std::string& name, const int ifindex,
-                             const std::string& addr) {
+    static Iface createIface(const std::string& name, const int ifindex) {
         Iface iface(name, ifindex);
-        iface.addAddress(IOAddress(addr));
         if (name == "lo") {
             iface.flag_loopback_ = true;
         }
@@ -206,7 +214,30 @@ public:
         return (iface);
     }
 
-    /// @brief Modified flags on the interface.
+    /// @brief Checks if the specified interface has a socket bound to a
+    /// specified adddress.
+    ///
+    /// @param iface_name A name of the interface.
+    /// @param addr An address to be checked for binding.
+    ///
+    /// @return true if there is a socket bound to the specified address.
+    bool isBound(const std::string& iface_name, const std::string& addr) {
+        Iface* iface = getIface(iface_name);
+        if (iface == NULL) {
+            ADD_FAILURE() << "the interface " << iface_name << " doesn't exist";
+            return (false);
+        }
+        const Iface::SocketCollection& sockets = iface->getSockets();
+        for (Iface::SocketCollection::const_iterator sock = sockets.begin();
+             sock != sockets.end(); ++sock) {
+            if (sock->addr_ == IOAddress(addr)) {
+                return (true);
+            }
+        }
+        return (false);
+    }
+
+    /// @brief Modify flags on the interface.
     ///
     /// @param name A name of the interface.
     /// @param loopback A new value of the loopback flag.
@@ -215,14 +246,16 @@ public:
     /// @param inactive A new value of the inactive flag.
     void setIfaceFlags(const std::string& name, const bool loopback,
                        const bool up, const bool running,
-                       const bool inactive) {
+                       const bool inactive4,
+                       const bool inactive6) {
         for (IfaceMgr::IfaceCollection::iterator iface = ifaces_.begin();
              iface != ifaces_.end(); ++iface) {
             if (iface->getName() == name) {
                 iface->flag_loopback_ = loopback;
                 iface->flag_up_ = up;
                 iface->flag_running_ = running;
-                iface->inactive4_ = inactive;
+                iface->inactive4_ = inactive4;
+                iface->inactive6_ = inactive6;
             }
         }
     }
@@ -245,6 +278,38 @@ public:
     ~IfaceMgrTest() {
     }
 
+    /// @brief Tests the number of IPv6 sockets on interface
+    ///
+    /// This function checks the expected number of open IPv6 sockets on the
+    /// specified interface. On non-Linux systems, sockets are bound to a
+    /// link-local address and the number of unicast addresses specified.
+    /// On Linux systems, there is one more socket bound to a ff02::1:2
+    /// multicast address.
+    ///
+    /// @param iface An interface on which sockets are open.
+    /// @param unicast_num A number of unicast addresses bound.
+    void checkSocketsCount6(const Iface& iface, const int unicast_num) {
+        // On local-loopback interface, there should be no sockets.
+        if (iface.flag_loopback_) {
+            ASSERT_TRUE(iface.getSockets().empty())
+                << "expected empty socket set on loopback interface "
+                << iface.getName();
+            return;
+        }
+#if defined OS_LINUX
+        // On Linux, there is an additional socket bound to ff02::1:2.
+        ASSERT_EQ(unicast_num + 2, iface.getSockets().size())
+            << "invalid number of sockets on interface "
+            << iface.getName();
+#else
+        // On non-Linux, there is no additional socket.
+        ASSERT_EQ(unicast_num + 1, iface.getSockets().size())
+            << "invalid number of sockets on interface "
+            << iface.getName();
+
+#endif
+    }
+
     // Get ther number of IPv4 or IPv6 sockets on the loopback interface
     int getOpenSocketsCount(const Iface& iface, uint16_t family) const {
         // Get all sockets.
@@ -1082,12 +1147,12 @@ TEST_F(IfaceMgrTest, setPacketFilter6) {
     ASSERT_TRUE(iface_mgr);
 
     // Try to set NULL packet filter object and make sure it is rejected.
-    boost::shared_ptr<test::PktFilter6Stub> custom_packet_filter;
+    boost::shared_ptr<PktFilter6Stub> custom_packet_filter;
     EXPECT_THROW(iface_mgr->setPacketFilter(custom_packet_filter),
                  isc::dhcp::InvalidPacketFilter);
 
     // Create valid object and check if it can be set.
-    custom_packet_filter.reset(new test::PktFilter6Stub());
+    custom_packet_filter.reset(new PktFilter6Stub());
     ASSERT_TRUE(custom_packet_filter);
     ASSERT_NO_THROW(iface_mgr->setPacketFilter(custom_packet_filter));
 
@@ -1295,7 +1360,7 @@ TEST_F(IfaceMgrTest, openSockets4IfaceDown) {
     // - is "down" (not up)
     // - is not running
     // - is active (is not inactive)
-    ifacemgr.setIfaceFlags("eth0", false, false, true, false);
+    ifacemgr.setIfaceFlags("eth0", false, false, true, false, false);
     ASSERT_FALSE(ifacemgr.getIface("eth0")->flag_up_);
     ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
 
@@ -1326,7 +1391,7 @@ TEST_F(IfaceMgrTest, openSockets4IfaceInactive) {
     // - is up
     // - is running
     // - is inactive
-    ifacemgr.setIfaceFlags("eth1", false, true, true, true);
+    ifacemgr.setIfaceFlags("eth1", false, true, true, true, false);
     ASSERT_TRUE(ifacemgr.getIface("eth1")->inactive4_);
     ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
 
@@ -1403,6 +1468,182 @@ TEST_F(IfaceMgrTest, openSocket4ErrorHandler) {
 
 }
 
+// This test checks that the sockets are open and bound to link local addresses
+// only, if unicast addresses are not specified.
+TEST_F(IfaceMgrTest, openSockets6LinkLocal) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that the number of sockets is correct on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0);
+
+    // Sockets on eth0 should be bound to link-local and should not be bound
+    // to global unicast address, even though this address is configured on
+    // the eth0.
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    // Socket on eth1 should be bound to link local only.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+    EXPECT_TRUE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+}
+
+// This test checks that the sockets are opened and bound to link local
+// and unicast addresses which have been explicitly specified.
+TEST_F(IfaceMgrTest, openSockets6Unicast) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Configure the eth0 to open socket on the unicast address, apart
+    // from link-local address.
+    ifacemgr.getIface("eth0")->addUnicast(IOAddress("2001:db8:1::1"));
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that we have correct number of sockets on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 1); // one unicast address.
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0);
+
+    // eth0 should have two sockets, one bound to link-local, another one
+    // bound to unicast address.
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    // eth1 should have one socket, bound to link-local address.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+    EXPECT_TRUE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+
+}
+
+// This test checks that no sockets are open for the interface which is down.
+TEST_F(IfaceMgrTest, openSockets6IfaceDown) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Configure the eth0 to open socket on the unicast address, apart
+    // from link-local address.
+    ifacemgr.getIface("eth0")->addUnicast(IOAddress("2001:db8:1::1"));
+
+    // Boolean parameters specify that eth0 is:
+    // - not a loopback
+    // - is "down" (not up)
+    // - is not running
+    // - is active for both v4 and v6
+    ifacemgr.setIfaceFlags("eth0", false, false, false, false, false);
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that we have correct number of sockets on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    // There should be no sockets on eth0 because interface is down.
+    ASSERT_TRUE(ifacemgr.getIface("eth0")->getSockets().empty());
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0);
+
+    // eth0 should have no sockets because the interface is down.
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+    // eth1 should have one socket, bound to link-local address.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+
+}
+
+// This test checks that no sockets are open for the interface which is
+// inactive.
+TEST_F(IfaceMgrTest, openSockets6IfaceInactive) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Configure the eth0 to open socket on the unicast address, apart
+    // from link-local address.
+    ifacemgr.getIface("eth0")->addUnicast(IOAddress("2001:db8:1::1"));
+
+    // Boolean parameters specify that eth1 is:
+    // - not a loopback
+    // - is up
+    // - is running
+    // - is active for v4
+    // - is inactive for v6
+    ifacemgr.setIfaceFlags("eth1", false, true, true, false, true);
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that we have correct number of sockets on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 1); // one unicast address
+    // There should be no sockets on eth1 because interface is inactive
+    ASSERT_TRUE(ifacemgr.getIface("eth1")->getSockets().empty());
+
+    // eth0 should have one socket bound to a link-local address, another one
+    // bound to unicast address.
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+
+    // eth1 shouldn't have a socket bound to link local address because
+    // interface is inactive.
+    EXPECT_FALSE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+    EXPECT_FALSE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+
+}
 
 // Test the Iface structure itself
 TEST_F(IfaceMgrTest, iface) {