Browse Source

[3288] Implemented OS-specific function which opens socket on multicast.

Marcin Siodelski 11 years ago
parent
commit
85ceb8b8c5

+ 1 - 0
src/lib/dhcp/Makefile.am

@@ -19,6 +19,7 @@ libb10_dhcp___la_SOURCES += duid.cc duid.h
 libb10_dhcp___la_SOURCES += hwaddr.cc hwaddr.h
 libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
 libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc
+libb10_dhcp___la_SOURCES += iface_mgr_error_handler.h
 libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
 libb10_dhcp___la_SOURCES += iface_mgr_sun.cc
 libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h

+ 7 - 89
src/lib/dhcp/iface_mgr.cc

@@ -22,6 +22,7 @@
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/iface_mgr_error_handler.h>
 #include <dhcp/pkt_filter_inet.h>
 #include <dhcp/pkt_filter_inet6.h>
 #include <exceptions/exceptions.h>
@@ -37,40 +38,6 @@
 #include <string.h>
 #include <sys/select.h>
 
-/// @brief A macro which handles an error in IfaceMgr.
-///
-/// There are certain cases when IfaceMgr may hit an error which shouldn't
-/// result in interruption of the function processing. A typical case is
-/// the function which opens sockets on available interfaces for a DHCP
-/// server. If this function fails to open a socket on a specific interface
-/// (for example, there is another socket already open on this interface
-/// and bound to the same address and port), it is desired that the server
-/// logs a warning but will try to open sockets on other interfaces. In order
-/// to log an error, the IfaceMgr will use the error handler function provided
-/// by the server and pass an error string to it. When the handler function
-/// returns, the IfaceMgr will proceed to open other sockets. It is allowed
-/// that the error handler function is not installed (is NULL). In these
-/// cases it is expected that the exception is thrown instead. A possible
-/// solution would be to enclose this conditional behavior in a function.
-/// However, despite the hate for macros, the macro seems to be a bit
-/// better solution in this case as it allows to convenietly pass an
-/// error string in a stream (not as a string).
-///
-/// @param ex_type Exception to be thrown if error_handler is NULL.
-/// @param handler Error handler function to be called or NULL to indicate
-/// that exception should be thrown instead.
-/// @param stream stream object holding an error string.
-#define IFACEMGR_ERROR(ex_type, handler, stream) \
-{ \
-    std::ostringstream oss__; \
-    oss__ << stream; \
-    if (handler) { \
-        handler(oss__.str()); \
-    } else { \
-        isc_throw(ex_type, oss__); \
-    } \
-} \
-
 using namespace std;
 using namespace isc::asiolink;
 using namespace isc::util::io::internal;
@@ -183,7 +150,7 @@ bool Iface::delAddress(const isc::asiolink::IOAddress& addr) {
     return (false);
 }
 
-bool Iface::delSocket(uint16_t sockfd) {
+bool Iface::delSocket(const uint16_t sockfd) {
     list<SocketInfo>::iterator sock = sockets_.begin();
     while (sock!=sockets_.end()) {
         if (sock->sockfd_ == sockfd) {
@@ -531,60 +498,13 @@ IfaceMgr::openSockets6(const uint16_t port,
                 continue;
             }
 
-            // Open socket and join multicast group only if the interface
-            // is multicast-capable.
-            // @todo The DHCPv6 requires multicast so we may want to think
-            // whether we want to open the socket on a multicast-incapable
-            // interface or not. For now, we prefer to be liberal and allow
-            // it for some odd use cases which may utilize non-multicast
-            // interfaces. Perhaps a warning should be emitted if the
-            // interface is not a multicast one.
-
-            // The sock variable will hold a socket descriptor. It may be
-            // used to close a socket if the function fails to bind to
-            // multicast address on Linux systems. Because we only bind
-            // a socket to multicast address on Linux, on other systems
-            // the sock variable will be initialized but unused. We have
-            // to suppress the cppcheck warning which shows up on non-Linux
-            // systems.
-            // cppcheck-suppress variableScope
-            int sock;
-            try {
-                // cppcheck-suppress unreadVariable
-                sock = openSocket(iface->getName(), *addr, port,
-                                  iface->flag_multicast_);
-
-            } catch (const Exception& ex) {
-                IFACEMGR_ERROR(SocketConfigError, error_handler,
-                               "Failed to open link-local socket on "
-                               " interface " << iface->getName() << ": "
-                               << ex.what());
-                continue;
-
+            // Run OS-specific function to open a socket on link-local address
+            // and join multicast group (non-Linux OSes), or open two sockets and
+            // bind one to link-local, another one to multicast address.
+            if (openMulticastSocket(*iface, *addr, port, error_handler)) {
+                ++count;
             }
 
-            count++;
-
-            /// @todo: Remove this ifdef once we start supporting BSD systems.
-#if defined(OS_LINUX)
-            // To receive multicast traffic, Linux requires binding socket to
-            // a multicast group. That in turn doesn't work on NetBSD.
-            if (iface->flag_multicast_) {
-                try {
-                    openSocket(iface->getName(),
-                               IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
-                               port);
-                } catch (const Exception& ex) {
-                    // Delete previously opened socket.
-                    iface->delSocket(sock);
-                    IFACEMGR_ERROR(SocketConfigError, error_handler,
-                                   "Failed to open multicast socket on"
-                                   " interface " << iface->getName()
-                                   << ", reason: " << ex.what());
-                    continue;
-                }
-            }
-#endif
         }
     }
     return (count > 0);
@@ -809,7 +729,6 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
     return IOAddress(local_address);
 }
 
-
 int
 IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
                       const bool join_multicast) {
@@ -1131,6 +1050,5 @@ IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
               << " does not have any suitable IPv4 sockets open.");
 }
 
-
 } // end of namespace isc::dhcp
 } // end of namespace isc

+ 24 - 0
src/lib/dhcp/iface_mgr.h

@@ -34,6 +34,7 @@ namespace isc {
 
 namespace dhcp {
 
+
 /// @brief IfaceMgr exception thrown thrown when interface detection fails.
 class IfaceDetectError : public Exception {
 public:
@@ -1002,6 +1003,29 @@ private:
                     const uint16_t port);
 
 
+    /// @brief Open an IPv6 socket with multicast support.
+    ///
+    /// This function opens socket(s) to allow reception of the DHCPv6 sent
+    /// to multicast address. It opens an IPv6 socket, binds it to link-local
+    /// address and joins multicast group (on non-Linux systems) or opens two
+    /// IPv6 sockets and binds one of them to link-local address and another
+    /// one to multicast address (on Linux systems).
+    ///
+    /// @note This function is intended to be called internally by the
+    /// @c IfaceMgr::openSockets6. It is not intended to be called from any
+    /// other function.
+    ///
+    /// @param iface Interface on which socket should be open.
+    /// @param addr Link-local address to bind the socket to.
+    /// @param port Port number to bind socket to.
+    /// @param error_handler Error handler function to be called when an
+    /// error occurs during opening a socket, or NULL if exception should
+    /// be thrown upon error.
+    bool openMulticastSocket(Iface& iface,
+                             const isc::asiolink::IOAddress addr,
+                             const uint16_t port,
+                             IfaceMgrErrorMsgCallback error_handler = NULL);
+
     /// Holds instance of a class derived from PktFilter, used by the
     /// IfaceMgr to open sockets and send/receive packets through these
     /// sockets. It is possible to supply custom object using

+ 23 - 0
src/lib/dhcp/iface_mgr_bsd.cc

@@ -17,6 +17,7 @@
 #if defined(OS_BSD)
 
 #include <dhcp/iface_mgr.h>
+#include <dhcp/iface_mgr_error_handler.h>
 #include <dhcp/pkt_filter_inet.h>
 #include <exceptions/exceptions.h>
 
@@ -149,6 +150,28 @@ IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
     setPacketFilter(PktFilterPtr(new PktFilterInet()));
 }
 
+bool
+IfaceMgr::openMulticastSocket(Iface& iface,
+                              const isc::asiolink::IOAddress addr,
+                              const uint16_t port,
+                              IfaceMgrErrorMsgCallback error_handler) {
+    try {
+        // This should open a socket, bound it to link-local address
+        // and join multicast group.
+        openSocket(iface.getName(), addr, port,
+                   iface.flag_multicast_);
+
+    } catch (const Exception& ex) {
+        IFACEMGR_ERROR(SocketConfigError, error_handler,
+                       "Failed to open link-local socket on "
+                       " interface " << iface.getName() << ": "
+                       << ex.what());
+        return (false);
+
+    }
+    return (true);
+}
+
 
 } // end of isc::dhcp namespace
 } // end of dhcp namespace

+ 52 - 0
src/lib/dhcp/iface_mgr_error_handler.h

@@ -0,0 +1,52 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef IFACE_MGR_ERROR_HANDLER_H
+#define IFACE_MGR_ERROR_HANDLER_H
+
+/// @brief A macro which handles an error in IfaceMgr.
+///
+/// There are certain cases when IfaceMgr may hit an error which shouldn't
+/// result in interruption of the function processing. A typical case is
+/// the function which opens sockets on available interfaces for a DHCP
+/// server. If this function fails to open a socket on a specific interface
+/// (for example, there is another socket already open on this interface
+/// and bound to the same address and port), it is desired that the server
+/// logs a warning but will try to open sockets on other interfaces. In order
+/// to log an error, the IfaceMgr will use the error handler function provided
+/// by the server and pass an error string to it. When the handler function
+/// returns, the IfaceMgr will proceed to open other sockets. It is allowed
+/// that the error handler function is not installed (is NULL). In these
+/// cases it is expected that the exception is thrown instead. A possible
+/// solution would be to enclose this conditional behavior in a function.
+/// However, despite the hate for macros, the macro seems to be a bit
+/// better solution in this case as it allows to convenietly pass an
+/// error string in a stream (not as a string).
+///
+/// @param ex_type Exception to be thrown if error_handler is NULL.
+/// @param handler Error handler function to be called or NULL to indicate
+/// that exception should be thrown instead.
+/// @param stream stream object holding an error string.
+#define IFACEMGR_ERROR(ex_type, handler, stream) \
+{ \
+    std::ostringstream oss__; \
+    oss__ << stream; \
+    if (handler) { \
+        handler(oss__.str()); \
+    } else { \
+        isc_throw(ex_type, oss__); \
+    } \
+} \
+
+#endif // IFACE_MGR_ERROR_HANDLER_H

+ 55 - 0
src/lib/dhcp/iface_mgr_linux.cc

@@ -33,6 +33,7 @@
 
 #include <asiolink/io_address.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/iface_mgr_error_handler.h>
 #include <dhcp/pkt_filter_inet.h>
 #include <dhcp/pkt_filter_lpf.h>
 #include <exceptions/exceptions.h>
@@ -533,6 +534,60 @@ bool IfaceMgr::os_receive4(struct msghdr&, Pkt4Ptr&) {
     return (true);
 }
 
+bool
+IfaceMgr::openMulticastSocket(Iface& iface,
+                              const isc::asiolink::IOAddress addr,
+                              const uint16_t port,
+                              IfaceMgrErrorMsgCallback error_handler) {
+    // This variable will hold a descriptor of the socket bound to
+    // link-local address. It may be required for us to close this
+    // socket if an attempt to open and bind a socket to multicast
+    // address fails.
+    int sock;
+    try {
+        sock = openSocket(iface.getName(), addr, port,
+                          iface.flag_multicast_);
+
+    } catch (const Exception& ex) {
+        IFACEMGR_ERROR(SocketConfigError, error_handler,
+                       "Failed to open link-local socket on "
+                       " interface " << iface.getName() << ": "
+                       << ex.what());
+        return (false);
+
+    }
+
+    // To receive multicast traffic, Linux requires binding socket to
+    // the multicast address.
+
+    /// @todo The DHCPv6 requires multicast so we may want to think
+    /// whether we want to open the socket on a multicast-incapable
+    /// interface or not. For now, we prefer to be liberal and allow
+    /// it for some odd use cases which may utilize non-multicast
+    /// interfaces. Perhaps a warning should be emitted if the
+    /// interface is not a multicast one.
+    if (iface.flag_multicast_) {
+        try {
+            openSocket(iface.getName(),
+                       IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+                       port);
+        } catch (const Exception& ex) {
+            // An attempt to open and bind a socket to multicast addres
+            // has failed. We have to close the socket we previously
+            // bound to link-local address - this is everything or
+            // nothing strategy.
+            iface.delSocket(sock);
+            IFACEMGR_ERROR(SocketConfigError, error_handler,
+                           "Failed to open multicast socket on"
+                           " interface " << iface.getName()
+                           << ", reason: " << ex.what());
+            return (false);
+        }
+    }
+    // Both sockets have opened successfully.
+    return (true);
+}
+
 } // end of isc::dhcp namespace
 } // end of isc namespace
 

+ 23 - 0
src/lib/dhcp/iface_mgr_sun.cc

@@ -17,6 +17,7 @@
 #if defined(OS_SUN)
 
 #include <dhcp/iface_mgr.h>
+#include <dhcp/iface_mgr_error_handler.h>
 #include <dhcp/pkt_filter_inet.h>
 #include <exceptions/exceptions.h>
 
@@ -153,6 +154,28 @@ IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
     setPacketFilter(PktFilterPtr(new PktFilterInet()));
 }
 
+bool
+IfaceMgr::openMulticastSocket(Iface& iface,
+                              const isc::asiolink::IOAddress addr,
+                              const uint16_t port,
+                              IfaceMgrErrorMsgCallback error_handler) {
+    try {
+        // This should open a socket, bound it to link-local address
+        // and join multicast group.
+        openSocket(iface.getName(), addr, port,
+                   iface.flag_multicast_);
+
+    } catch (const Exception& ex) {
+        IFACEMGR_ERROR(SocketConfigError, error_handler,
+                       "Failed to open link-local socket on "
+                       " interface " << iface.getName() << ": "
+                       << ex.what());
+        return (false);
+
+    }
+    return (true);
+}
+
 } // end of isc::dhcp namespace
 } // end of dhcp namespace
 

+ 0 - 0
src/lib/dhcp_ddns/s-messages