Parcourir la source

[2902] Do not allow to set new packet filter when there are sockets open.

Marcin Siodelski il y a 12 ans
Parent
commit
85d28a85b0
3 fichiers modifiés avec 63 ajouts et 15 suppressions
  1. 30 0
      src/lib/dhcp/iface_mgr.cc
  2. 12 7
      src/lib/dhcp/iface_mgr.h
  3. 21 8
      src/lib/dhcp/tests/iface_mgr_unittest.cc

+ 30 - 0
src/lib/dhcp/iface_mgr.cc

@@ -203,6 +203,36 @@ IfaceMgr::isDirectResponseSupported() const {
     return (packet_filter_->isDirectResponseSupported());
 }
 
+void
+IfaceMgr::setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
+    // Do not allow NULL pointer.
+    if (!packet_filter) {
+        isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
+    }
+    // Different packet filters use different socket types. It does not make
+    // sense to allow the change of packet filter when there are IPv4 sockets
+    // open because they can't be used by the receive/send functions of the
+    // new packet filter. Below, we check that there are no open IPv4 sockets.
+    // If we find at least one, we have to fail. However, caller still has a
+    // chance to replace the packet filter if he closes sockets explicitly.
+    for (IfaceCollection::const_iterator iface = ifaces_.begin();
+         iface != ifaces_.end(); ++iface) {
+        const Iface::SocketCollection& sockets = iface->getSockets();
+        for (Iface::SocketCollection::const_iterator sock = sockets.begin();
+             sock != sockets.end(); ++sock) {
+            // There is at least one socket open, so we have to fail.
+            if (sock->family_ == AF_INET) {
+                isc_throw(PacketFilterChangeDenied, "it is not allowed to set new packet"
+                          << " filter when there are open IPv4 sockets - need"
+                          << " to close them first");
+            }
+        }
+    }
+    // Everything is fine, so replace packet filter.
+    packet_filter_ = packet_filter;
+}
+
+
 void IfaceMgr::stubDetectIfaces() {
     string ifaceName;
     const string v4addr("127.0.0.1"), v6addr("::1");

+ 12 - 7
src/lib/dhcp/iface_mgr.h

@@ -39,6 +39,13 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+/// @brief Exception thrown when it is not allowed to set new Packet Filter.
+class PacketFilterChangeDenied : public Exception {
+public:
+    PacketFilterChangeDenied(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief IfaceMgr exception thrown thrown when socket opening
 /// or configuration failed.
 class SocketConfigError : public Exception {
@@ -602,18 +609,16 @@ public:
     /// Packet Filters expose low-level functions handling sockets opening
     /// and sending/receiving packets through those sockets. This function
     /// sets custom Packet Filter (represented by a class derived from PktFilter)
-    /// to be used by IfaceMgr.
+    /// to be used by IfaceMgr. Note that, there must be no IPv4 sockets
+    /// when this function is called. Call closeSockets(AF_INET) to close
+    /// all hanging IPv4 sockets opened by the current packet filter object.
     ///
     /// @param packet_filter new packet filter to be used by IfaceMgr to send/receive
     /// packets and open sockets.
     ///
     /// @throw InvalidPacketFilter if provided packet filter object is NULL.
-    void setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
-        if (!packet_filter) {
-            isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
-        }
-        packet_filter_ = packet_filter;
-    }
+    /// @throw PacketFilterChangeDenied if there are open IPv4 sockets
+    void setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter);
 
     /// @brief Set Packet Filter object to handle send/receive packets.
     ///

+ 21 - 8
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -689,19 +689,23 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
     // open sockets on the same ports.
     ifacemgr->closeSockets();
 
-    // The following test is currently disabled for OSes other than
-    // Linux because interface detection is not implemented on them.
-    // @todo enable this test for all OSes once interface detection
-    // is implemented.
-#if defined(OS_LINUX)
+    // The check below has been commented out. It verified the ability
+    // to open suitable socket for sending broadcast request. However,
+    // there is no guarantee for such test to work on all systems
+    // because some systems may have no broadcast capable interfaces at all.
+
+/* #if defined(OS_LINUX)
     // Open v4 socket to connect to broadcast address.
     int socket3  = 0;
     IOAddress bcastAddr("255.255.255.255");
-    EXPECT_NO_THROW(
+    try {
         socket3 = ifacemgr->openSocketFromRemoteAddress(bcastAddr, PORT2);
-    );
+    } catch (const Exception& ex) {
+        std::cout << ex.what() << std::endl;
+        FAIL();
+    }
     EXPECT_GT(socket3, 0);
-#endif
+#endif */
 
     // Do not call closeSockets() because it is called by IfaceMgr's
     // virtual destructor.
@@ -919,6 +923,15 @@ TEST_F(IfaceMgrTest, setPacketFilter) {
     EXPECT_TRUE(custom_packet_filter->open_socket_called_);
     // This function always returns fake socket descriptor equal to 1024.
     EXPECT_EQ(1024, socket1);
+
+    // Replacing current packet filter object while there are IPv4
+    // sockets open is not allowed!
+    EXPECT_THROW(iface_mgr->setPacketFilter(custom_packet_filter),
+                 PacketFilterChangeDenied);
+
+    // So, let's close the open IPv4 sockets and retry. Now it should succeed.
+    iface_mgr->closeSockets(AF_INET);
+    EXPECT_NO_THROW(iface_mgr->setPacketFilter(custom_packet_filter));
 }
 
 #if defined OS_LINUX