Browse Source

[2902] DHCPv4 server will try to use 'direct response' feature if possible.

Marcin Siodelski 12 years ago
parent
commit
8b0cb0ded8

+ 7 - 2
src/bin/dhcp4/dhcp4_srv.cc

@@ -61,8 +61,13 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast)
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
     try {
         // First call to instance() will create IfaceMgr (it's a singleton)
-        // it may throw something if things go wrong
-        IfaceMgr::instance();
+        // it may throw something if things go wrong.
+        // The 'true' value of in the call to setMatchingPacketFilter imposes
+        // that IfaceMgr will try to use the mechanism to respond directly
+        // to the client which doesn't have address assigned. This capability
+        // may be lacking on some OSes, so there is no guarantee that server
+        // will be able to respond directly.
+        IfaceMgr::instance().setMatchingPacketFilter(true);
 
         if (port) {
             // open sockets only if port is non-zero. Port 0 is used

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

@@ -157,6 +157,11 @@ IfaceMgr::~IfaceMgr() {
     closeSockets();
 }
 
+bool
+IfaceMgr::isDirectResponseSupported() const {
+    return (packet_filter_->isDirectResponseSupported());
+}
+
 void IfaceMgr::stubDetectIfaces() {
     string ifaceName;
     const string v4addr("127.0.0.1"), v6addr("::1");

+ 17 - 8
src/lib/dhcp/iface_mgr.h

@@ -39,13 +39,6 @@ public:
         isc::Exception(file, line, what) { };
 };
 
-/// @brief IfaceMgr exception thrown when invalid packet filter object specified.
-class InvalidPacketFilter : public Exception {
-public:
-    InvalidPacketFilter(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 {
@@ -324,7 +317,7 @@ public:
     /// the client.
     ///
     /// @return true if direct response is supported.
-    bool isDirectResponseSupported();
+    bool isDirectResponseSupported() const;
 
     /// @brief Returns interface with specified interface index
     ///
@@ -580,6 +573,22 @@ public:
         packet_filter_ = packet_filter;
     }
 
+    /// @brief Set Packet Filter object to handle send/receive packets.
+    ///
+    /// This function sets Packet Filter object to be used by IfaceMgr,
+    /// appropriate for the current OS. They will vary depending on the
+    /// OS being used if the function argument is set 'true'. There is
+    /// no guarantee that there is an implementation that supports this
+    /// feature on a particular OS. If there isn't the PktFilterInet
+    /// object will be set. If the argument is set to 'false' then
+    /// PktFilterInet object instance will be set as the Packet Filter
+    /// regrdaless of the OS.
+    ///
+    /// @param direct_response_desired specifies whether the Packet Filter
+    /// object being set should support direct responses to the host
+    /// not having address assigned.
+    void setMatchingPacketFilter(const bool direct_response_desired = false);
+
     /// A value of socket descriptor representing "not specified" state.
     static const int INVALID_SOCKET = -1;
 

+ 7 - 5
src/lib/dhcp/iface_mgr_bsd.cc

@@ -34,11 +34,6 @@ IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 
-bool
-IfaceMgr::isDirectResponseSupported() {
-    return (false);
-}
-
 void IfaceMgr::os_send4(struct msghdr& /*m*/,
                         boost::scoped_array<char>& /*control_buf*/,
                         size_t /*control_buf_len*/,
@@ -54,6 +49,13 @@ bool IfaceMgr::os_receive4(struct msghdr& /*m*/, Pkt4Ptr& /*pkt*/) {
   return (true); // pretend that we have everything set up for reception.
 }
 
+void
+IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
+    boost::shared_ptr<PktFilter> pkt_filter(new PktFilterInet());
+    setPacketFilter(pkt_filter);
+}
+
+
 } // end of isc::dhcp namespace
 } // end of dhcp namespace
 

+ 14 - 5
src/lib/dhcp/iface_mgr_linux.cc

@@ -33,6 +33,8 @@
 
 #include <asiolink/io_address.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter_inet.h>
+#include <dhcp/pkt_filter_lpf.h>
 #include <exceptions/exceptions.h>
 #include <util/io/sockaddr_util.h>
 
@@ -494,11 +496,6 @@ void IfaceMgr::detectIfaces() {
     nl.release_list(addr_info);
 }
 
-bool
-IfaceMgr::isDirectResponseSupported() {
-    return (false);
-}
-
 /// @brief sets flag_*_ fields.
 ///
 /// This implementation is OS-specific as bits have different meaning
@@ -515,6 +512,18 @@ void Iface::setFlags(uint32_t flags) {
     flag_broadcast_ = flags & IFF_BROADCAST;
 }
 
+void
+IfaceMgr::setMatchingPacketFilter(const bool direct_response_desired) {
+    if (direct_response_desired) {
+        boost::shared_ptr<PktFilter> pkt_filter(new PktFilterLPF());
+        setPacketFilter(pkt_filter);
+
+    } else {
+        boost::shared_ptr<PktFilter> pkt_filter(new PktFilterInet());
+        setPacketFilter(pkt_filter);
+
+    }
+}
 
 void IfaceMgr::os_send4(struct msghdr&, boost::scoped_array<char>&,
                         size_t, const Pkt4Ptr&) {

+ 6 - 5
src/lib/dhcp/iface_mgr_sun.cc

@@ -34,11 +34,6 @@ IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 
-bool
-IfaceMgr::isDirectResponseSupported() {
-    return (false);
-}
-
 void IfaceMgr::os_send4(struct msghdr& /*m*/,
                         boost::scoped_array<char>& /*control_buf*/,
                         size_t /*control_buf_len*/,
@@ -54,6 +49,12 @@ bool IfaceMgr::os_receive4(struct msghdr& /*m*/, Pkt4Ptr& /*pkt*/) {
   return (true); // pretend that we have everything set up for reception.
 }
 
+void
+IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
+    boost::shared_ptr<PktFilter> pkt_filter(new PktFilterInet());
+    setPacketFilter(pkt_filter);
+}
+
 } // end of isc::dhcp namespace
 } // end of dhcp namespace
 

+ 20 - 0
src/lib/dhcp/pkt_filter.h

@@ -15,12 +15,20 @@
 #ifndef PKT_FILTER_H
 #define PKT_FILTER_H
 
+#include <dhcp/pkt4.h>
 #include <asiolink/io_address.h>
 #include <boost/shared_ptr.hpp>
 
 namespace isc {
 namespace dhcp {
 
+/// @brief Exception thrown when invalid packet filter object specified.
+class InvalidPacketFilter : public Exception {
+public:
+    InvalidPacketFilter(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 struct SocketInfo;
 
 /// Forward declaration to the class representing interface
@@ -46,6 +54,18 @@ public:
     /// @brief Virtual Destructor
     virtual ~PktFilter() { }
 
+    /// @brief Check if packet can be sent to the host without address directly.
+    ///
+    /// Checks if the Packet Filter class has capability to send a packet
+    /// directly to the client having no address assigned. This capability
+    /// is used by DHCPv4 servers which respond to the clients they assign
+    /// addresses to. Not all classes derived from PktFilter support this
+    /// because it requires injection of the destination host HW address to
+    /// the link layer header of the packet.
+    ///
+    /// @return true of the direct response is supported.
+    virtual bool isDirectResponseSupported() const = 0;
+
     /// @brief Open socket.
     ///
     /// @param iface interface descriptor

+ 11 - 0
src/lib/dhcp/pkt_filter_inet.h

@@ -33,6 +33,17 @@ public:
     /// Allocates control buffer.
     PktFilterInet();
 
+    /// @brief Check if packet can be sent to the host without address directly.
+    ///
+    /// This Packet Filter sends packets through AF_INET datagram sockets, so
+    /// it can't inject the HW address of the destionation host into the packet.
+    /// Therefore this class does not support direct responses.
+    ///
+    /// @return false always.
+    virtual bool isDirectResponseSupported() const {
+        return (false);
+    }
+
     /// @brief Open socket.
     ///
     /// @param iface interface descriptor

+ 9 - 0
src/lib/dhcp/pkt_filter_lpf.h

@@ -32,6 +32,15 @@ namespace dhcp {
 class PktFilterLPF : public PktFilter {
 public:
 
+    /// @brief Check if packet can be sent to the host without address directly.
+    ///
+    /// This class supports direct responses to the host without address.
+    ///
+    /// @return true always.
+    virtual bool isDirectResponseSupported() const {
+        return (true);
+    }
+
     /// @brief Open socket.
     ///
     /// @param iface interface descriptor

+ 67 - 0
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -74,6 +74,10 @@ public:
         : open_socket_called_(false) {
     }
 
+    virtual bool isDirectResponseSupported() const {
+        return (false);
+    }
+
     /// Pretends to open socket. Only records a call to this function.
     virtual int openSocket(const Iface&,
                            const isc::asiolink::IOAddress&,
@@ -839,6 +843,69 @@ TEST_F(IfaceMgrTest, setPacketFilter) {
     EXPECT_EQ(1024, socket1);
 }
 
+#if defined OS_LINUX
+
+// This Linux specific test checks whether it is possible to use
+// IfaceMgr to figure out which Pakcket Filter object should be
+// used when direct responses to hosts, having no address assigned
+// are desired or not desired.
+TEST_F(IfaceMgrTest, setMatchingPacketFilter) {
+
+    // Create an instance of IfaceMgr.
+    boost::scoped_ptr<NakedIfaceMgr> iface_mgr(new NakedIfaceMgr());
+    ASSERT_TRUE(iface_mgr);
+
+    // Let IfaceMgr figure out which Packet Filter to use when
+    // direct response capability is not desired. It should pick
+    // PktFilterInet.
+    EXPECT_NO_THROW(iface_mgr->setMatchingPacketFilter(false));
+    // The PktFilterInet is supposed to report lack of direct
+    // response capability.
+    EXPECT_FALSE(iface_mgr->isDirectResponseSupported());
+
+    // There is working implementation of direct responses on Linux
+    // in PktFilterLPF. It uses Linux Packet Filtering as underlying
+    // mechanism. When direct responses are desired the object of
+    // this class should be set.
+    EXPECT_NO_THROW(iface_mgr->setMatchingPacketFilter(true));
+    // This object should report that direct responses are supported.
+    EXPECT_TRUE(iface_mgr->isDirectResponseSupported());
+}
+
+#else
+
+// This non-Linux specific test checks whether it is possible to use
+// IfaceMgr to figure out which Pakcket Filter object should be
+// used when direct responses to hosts, having no address assigned
+// are desired or not desired. Since direct responses aren't supported
+// on systems other than Linux the function under test should always
+// set object of PktFilterInet type as current Packet Filter. This
+// object does not support direct responses. Once implementation is
+// added on non-Linux systems the OS specific version of the test
+// will be removed.
+TEST_F(IfaceMgrTest, setMatchingPacketFilter) {
+
+    // Create an instance of IfaceMgr.
+    boost::scoped_ptr<NakedIfaceMgr> iface_mgr(new NakedIfaceMgr());
+    ASSERT_TRUE(iface_mgr);
+
+    // Let IfaceMgr figure out which Packet Filter to use when
+    // direct response capability is not desired. It should pick
+    // PktFilterInet.
+    EXPECT_NO_THROW(iface_mgr->setMatchingPacketFilter(false));
+    // The PktFilterInet is supposed to report lack of direct
+    // response capability.
+    EXPECT_FALSE(iface_mgr->isDirectResponseSupported());
+
+    // On non-Linux systems, we are missing the direct traffic
+    // implementation. Therefore, we expect that PktFilterInet
+    // object will be set.
+    EXPECT_NO_THROW(iface_mgr->setMatchingPacketFilter(true));
+    // This object should report lack of direct response capability.
+    EXPECT_FALSE(iface_mgr->isDirectResponseSupported());
+}
+
+#endif
 
 TEST_F(IfaceMgrTest, socket4) {
 

+ 11 - 0
src/lib/dhcp/tests/pkt_filter_inet_unittest.cc

@@ -77,6 +77,17 @@ public:
 
 };
 
+// This test verifies that the PktFilterInet class reports its lack
+// of capability to send packets to the host having no IP address
+// assigned.
+TEST_F(PktFilterInetTest, isDirectResponseSupported) {
+    // Create object under test.
+    PktFilterInet pkt_filter;
+    // This Packet Filter class does not support direct responses
+    // under any conditions.
+    EXPECT_FALSE(pkt_filter.isDirectResponseSupported());
+}
+
 // This test verifies that the INET datagram socket is correctly opened and
 // bound to the appropriate address and port.
 TEST_F(PktFilterInetTest, openSocket) {

+ 9 - 0
src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc

@@ -81,6 +81,15 @@ public:
 
 };
 
+// This test verifies that the PktFilterLPF class reports its capability
+// to send packets to the host having no IP address assigned.
+TEST_F(PktFilterLPFTest, isDirectResponseSupported) {
+    // Create object under test.
+    PktFilterLPF pkt_filter;
+    // Must support direct responses.
+    EXPECT_TRUE(pkt_filter.isDirectResponseSupported());
+}
+
 // All tests below require root privileges to execute successfully. If
 // they are run as non-root user they will fail due to insufficient privileges
 // to open raw network sockets. Therefore, they should remain disabled by default