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
#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>
#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;
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) {
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);
return IOAddress(local_address);
}
-
int
IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
const bool join_multicast) {
<< " does not have any suitable IPv4 sockets open.");
}
-
} // end of namespace isc::dhcp
} // end of namespace isc
namespace dhcp {
+
/// @brief IfaceMgr exception thrown thrown when interface detection fails.
class IfaceDetectError : public Exception {
public:
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
#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>
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
--- /dev/null
+// 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
#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>
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
#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>
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