status->set("multi-threading-enabled", Element::create(false));
}
+ // Iterate through the interfaces and get all the errors.
+ ElementPtr socket_errors(Element::createList());
+ for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
+ for (std::string const& error : interface->getErrors()) {
+ socket_errors->add(Element::create(error));
+ }
+ }
+
+ // Abstract the information from all sockets into a single status.
+ ElementPtr sockets(Element::createMap());
+ if (socket_errors->empty()) {
+ sockets->set("status", Element::create("ready"));
+ } else {
+ ReconnectCtlPtr const reconnect_ctl(
+ CfgMgr::instance().getCurrentCfg()->getCfgIface()->getReconnectCtl());
+ if (reconnect_ctl && reconnect_ctl->retriesLeft()) {
+ sockets->set("status", Element::create("retrying"));
+ } else {
+ sockets->set("status", Element::create("failed"));
+ }
+ sockets->set("errors", socket_errors);
+ }
+ status->set("sockets", sockets);
+
return (createAnswer(0, status));
}
status->set("multi-threading-enabled", Element::create(false));
}
+ // Iterate through the interfaces and get all the errors.
+ ElementPtr socket_errors(Element::createList());
+ for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
+ for (std::string const& error : interface->getErrors()) {
+ socket_errors->add(Element::create(error));
+ }
+ }
+
+ // Abstract the information from all sockets into a single status.
+ ElementPtr sockets(Element::createMap());
+ if (socket_errors->empty()) {
+ sockets->set("status", Element::create("ready"));
+ } else {
+ ReconnectCtlPtr const reconnect_ctl(
+ CfgMgr::instance().getCurrentCfg()->getCfgIface()->getReconnectCtl());
+ if (reconnect_ctl && reconnect_ctl->retriesLeft()) {
+ sockets->set("status", Element::create("retrying"));
+ } else {
+ sockets->set("status", Element::create("failed"));
+ }
+ sockets->set("errors", socket_errors);
+ }
+ status->set("sockets", sockets);
+
return (createAnswer(0, status));
}
int bcast_num = 0;
for (IfacePtr iface : ifaces_) {
+ // Clear any errors from previous socket opening.
+ iface->clearErrors();
+
// If the interface is inactive, there is nothing to do. Simply
// proceed to the next detected interface.
if (iface->inactive4_) {
// Relax the check when the loopback interface was explicitly
// allowed
if (iface->flag_loopback_ && !allow_loopback_) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"must not open socket on the loopback"
" interface " << iface->getName());
continue;
}
if (!iface->flag_up_) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"the interface " << iface->getName()
<< " is down");
continue;
}
if (!iface->flag_running_) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"the interface " << iface->getName()
<< " is not running");
continue;
IOAddress out_address("0.0.0.0");
if (!iface->getAddress4(out_address)) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"the interface " << iface->getName()
<< " has no usable IPv4 addresses configured");
continue;
// assume that binding to the device is not supported and we
// cease opening sockets and display the appropriate message.
if (is_open_as_broadcast && !isDirectResponseSupported() && bcast_num > 0) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"Binding socket to an interface is not"
" supported on this OS; therefore only"
" one socket listening to broadcast traffic"
is_open_as_broadcast,
is_open_as_broadcast);
} catch (const Exception& ex) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"Failed to open socket on interface "
<< iface->getName()
<< ", reason: "
int count = 0;
for (IfacePtr iface : ifaces_) {
+ // Clear any errors from previous socket opening.
+ iface->clearErrors();
+
+ // If the interface is inactive, there is nothing to do. Simply
+ // proceed to the next detected interface.
if (iface->inactive6_) {
continue;
}
// Relax the check when the loopback interface was explicitly
// allowed
if (iface->flag_loopback_ && !allow_loopback_) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"must not open socket on the loopback"
" interface " << iface->getName());
continue;
} else if (!iface->flag_up_) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"the interface " << iface->getName()
<< " is down");
continue;
} else if (!iface->flag_running_) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"the interface " << iface->getName()
<< " is not running");
continue;
try {
IfaceMgr::openSocket(iface->getName(), addr, port, false, false);
} catch (const Exception& ex) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"Failed to open unicast socket on interface "
<< iface->getName()
<< ", reason: " << ex.what());
// suppressing an exception in a system-specific function.
IfaceMgr::openMulticastSocket(*iface, addr, port);
} catch (const Exception& ex) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, iface,
"Failed to open multicast socket on interface "
<< iface->getName() << ", reason: " << ex.what());
continue;
return(enable_queue);
}
+void
+Iface::addError(std::string const& message) {
+ errors_.push_back(message);
+}
+
+void
+Iface::clearErrors() {
+ errors_.clear();
+}
+
+Iface::ErrorBuffer const&
+Iface::getErrors() const {
+ return errors_;
+}
+
} // end of namespace isc::dhcp
} // end of namespace isc
/// @todo: Add SocketCollectionConstIter type
typedef std::list<SocketInfo> SocketCollection;
+ /// @brief A type definition for a list of error messages
+ using ErrorBuffer = std::vector<std::string>;
+
+ /// @brief A smart pointer type for @ref ErrorBuffer
+ using ErrorBufferPtr = std::shared_ptr<ErrorBuffer>;
+
/// @brief Iface constructor.
///
/// Creates Iface object that represents network interface.
read_buffer_.resize(new_size);
}
+ /// @brief Add an error to the list of messages.
+ ///
+ /// @param message the error message
+ void addError(std::string const& message);
+
+ /// @brief Clears all errors.
+ void clearErrors();
+
+ /// @brief Get the consistent list of error messages.
+ ///
+ /// @return the list of messages
+ ErrorBuffer const& getErrors() const;
+
protected:
/// Socket used to send data.
SocketCollection sockets_;
///
/// See @c Iface manager description for details.
std::vector<uint8_t> read_buffer_;
+
+ /// @brief List of errors that occured since the last attempt to open sockets
+ ///
+ /// This list needs to always have a consistent view of the errors. They should all belong to
+ /// the same session of socket opening i.e. the same call to openSockets[46]. This is currently
+ /// ensured by openSockets[46] and all the places where these errors are being used i.e. the
+ /// status-get handler, being sequential.
+ ErrorBuffer errors_;
};
/// @brief Type definition for the pointer to an @c Iface object.
/// @brief Manager for DHCPv6 packet implementations and queues
PacketQueueMgr6Ptr packet_queue_mgr6_;
- /// DHCP packet receiver.
+ /// @brief DHCP packet receiver.
isc::util::WatchedThreadPtr dhcp_receiver_;
};
-}; // namespace isc::dhcp
-}; // namespace isc
+} // namespace isc::dhcp
+} // namespace isc
#endif // IFACE_MGR_H
openSocket(iface.getName(), addr, port, iface.flag_multicast_);
} catch (const Exception& ex) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, IfacePtr(),
"Failed to open link-local socket on "
"interface " << iface.getName() << ": "
<< ex.what());
/// @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 iface Pointer to the interafce for which the error is logged. Can be null.
/// @param stream stream object holding an error string.
-#define IFACEMGR_ERROR(ex_type, handler, stream) \
+#define IFACEMGR_ERROR(ex_type, handler, iface, stream) \
{ \
std::ostringstream ieoss__; \
ieoss__ << stream; \
+ std::string const error(ieoss__.str()); \
+ if (iface) { \
+ iface->addError(error); \
+ } \
if (handler) { \
- handler(ieoss__.str()); \
+ handler(error); \
} else { \
- isc_throw(ex_type, ieoss__.str()); \
+ isc_throw(ex_type, error); \
} \
} \
sock = openSocket(iface.getName(), addr, port, iface.flag_multicast_);
} catch (const Exception& ex) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, IfacePtr(),
"Failed to open link-local socket on "
"interface " << iface.getName() << ": "
<< ex.what());
// bound to link-local address - this is everything or
// nothing strategy.
iface.delSocket(sock);
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, IfacePtr(),
"Failed to open multicast socket on"
" interface " << iface.getName()
<< ", reason: " << ex.what());
openSocket(iface.getName(), addr, port, iface.flag_multicast_);
} catch (const Exception& ex) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
+ IFACEMGR_ERROR(SocketConfigError, error_handler, IfacePtr(),
"Failed to open link-local socket on "
"interface " << iface.getName() << ": "
<< ex.what());
void
CfgIface::openSockets(const uint16_t family, const uint16_t port,
- const bool use_bcast) const {
+ const bool use_bcast) {
// Close any open sockets because we're going to modify some properties
// of the IfaceMgr. Those modifications require that sockets are closed.
closeSockets();
LOG_WARN(dhcpsrv_logger, DHCPSRV_MULTIPLE_RAW_SOCKETS_PER_IFACE);
}
- auto reconnect_ctl = makeReconnectCtl();
- auto sopen = openSocketsWithRetry(reconnect_ctl, family, port, can_use_bcast);
+ reconnect_ctl_ = makeReconnectCtl();
+ auto sopen = openSocketsWithRetry(reconnect_ctl_, family, port, can_use_bcast);
if (!sopen) {
// If no socket were opened, log a warning because the server will
/// traffic should be received through the socket. This parameter is
/// ignored for IPv6.
void openSockets(const uint16_t family, const uint16_t port,
- const bool use_bcast = true) const;
+ const bool use_bcast = true);
/// @brief Puts the interface configuration into default state.
///
return (service_sockets_max_retries_);
}
+ /// @brief Get the reconnect controller.
+ ///
+ /// @return the reconnect controller
+ util::ReconnectCtlPtr getReconnectCtl() const {
+ return (reconnect_ctl_);
+ }
+
/// @brief Represents a callback invoked if all retries of the
/// opening sockets fail.
typedef std::function<void(util::ReconnectCtlPtr)> OpenSocketsFailedCallback;
/// Calls the @c CfgIface::openSocketsForFamily function and retry it if
/// socket opening fails.
///
+ /// @param reconnect_ctl Used to manage socket reconnection.
/// @param family Address family (AF_INET or AF_INET6).
/// @param port Port number to be used to bind sockets to.
/// @param can_use_bcast A boolean flag which indicates if the broadcast
/// traffic should be received through the socket and the raw sockets are
/// used. For the UDP sockets, we only handle the relayed (unicast)
/// traffic. This parameter is ignored for IPv6.
+ ///
/// @return True if at least one socket opened successfully.
static bool openSocketsWithRetry(util::ReconnectCtlPtr reconnect_ctl,
const uint16_t family, const uint16_t port,
/// @brief Indicates how outbound interface is selected for relayed traffic.
OutboundIface outbound_iface_;
+
+ /// @brief Used to manage socket reconnection.
+ util::ReconnectCtlPtr reconnect_ctl_;
};
/// @brief A pointer to the @c CfgIface .