From: Marcin Siodelski Date: Wed, 15 Jan 2014 19:31:58 +0000 (+0100) Subject: [3288] Implemented OS-specific function which opens socket on multicast. X-Git-Tag: bind10-1.2.0beta1-release~128^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=85ceb8b8c5fe2e586f512ade653dcd722330c24e;p=thirdparty%2Fkea.git [3288] Implemented OS-specific function which opens socket on multicast. --- diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index 17489278ee..640bac1b88 100644 --- a/src/lib/dhcp/Makefile.am +++ b/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 diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 8624c69b2a..e0c1b4435a 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -37,40 +38,6 @@ #include #include -/// @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::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 diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 3fde8674a0..5a26c5deb8 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/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 diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc index be73bcfc2e..08781727d0 100644 --- a/src/lib/dhcp/iface_mgr_bsd.cc +++ b/src/lib/dhcp/iface_mgr_bsd.cc @@ -17,6 +17,7 @@ #if defined(OS_BSD) #include +#include #include #include @@ -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 diff --git a/src/lib/dhcp/iface_mgr_error_handler.h b/src/lib/dhcp/iface_mgr_error_handler.h new file mode 100644 index 0000000000..c5ef5b401b --- /dev/null +++ b/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 diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc index dddeb52921..3dfd1dcbb4 100644 --- a/src/lib/dhcp/iface_mgr_linux.cc +++ b/src/lib/dhcp/iface_mgr_linux.cc @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -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 diff --git a/src/lib/dhcp/iface_mgr_sun.cc b/src/lib/dhcp/iface_mgr_sun.cc index fe2b0b3df2..ebbfbf0de2 100644 --- a/src/lib/dhcp/iface_mgr_sun.cc +++ b/src/lib/dhcp/iface_mgr_sun.cc @@ -17,6 +17,7 @@ #if defined(OS_SUN) #include +#include #include #include @@ -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 diff --git a/src/lib/dhcp_ddns/s-messages b/src/lib/dhcp_ddns/s-messages new file mode 100644 index 0000000000..e69de29bb2