From: Andrei Pavel Date: Thu, 21 Jul 2022 10:08:09 +0000 (+0300) Subject: [#2434] add socket status in response to status-get X-Git-Tag: Kea-2.2.0~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2904c6efccea13118aeeb7166af64f90b0ed5118;p=thirdparty%2Fkea.git [#2434] add socket status in response to status-get --- diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 431c13026f..6b62f0af02 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -748,6 +748,30 @@ ControlledDhcpv4Srv::commandStatusGetHandler(const string&, 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)); } diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 316937ebad..28c59ec21e 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -751,6 +751,30 @@ ControlledDhcpv6Srv::commandStatusGetHandler(const string&, 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)); } diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 9a346fbf8f..abb0f27d6f 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -523,6 +523,9 @@ IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast, 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_) { @@ -537,21 +540,21 @@ IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast, // 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; @@ -559,7 +562,7 @@ IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast, 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; @@ -590,7 +593,7 @@ IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast, // 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" @@ -610,7 +613,7 @@ IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast, 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: " @@ -649,6 +652,11 @@ IfaceMgr::openSockets6(const uint16_t port, 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; } @@ -661,17 +669,17 @@ IfaceMgr::openSockets6(const uint16_t port, // 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; @@ -690,7 +698,7 @@ IfaceMgr::openSockets6(const uint16_t port, 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()); @@ -734,7 +742,7 @@ IfaceMgr::openSockets6(const uint16_t port, // 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; @@ -1994,5 +2002,20 @@ IfaceMgr::configureDHCPPacketQueue(uint16_t family, data::ConstElementPtr queue_ 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 diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index e4380291ea..1e52bfdb9e 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -138,6 +138,12 @@ public: /// @todo: Add SocketCollectionConstIter type typedef std::list SocketCollection; + /// @brief A type definition for a list of error messages + using ErrorBuffer = std::vector; + + /// @brief A smart pointer type for @ref ErrorBuffer + using ErrorBufferPtr = std::shared_ptr; + /// @brief Iface constructor. /// /// Creates Iface object that represents network interface. @@ -392,6 +398,19 @@ public: 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_; @@ -457,6 +476,14 @@ private: /// /// See @c Iface manager description for details. std::vector 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. @@ -1571,11 +1598,11 @@ private: /// @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 diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc index 9b6cc9de33..bc77d967f1 100644 --- a/src/lib/dhcp/iface_mgr_bsd.cc +++ b/src/lib/dhcp/iface_mgr_bsd.cc @@ -168,7 +168,7 @@ IfaceMgr::openMulticastSocket(Iface& iface, 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()); diff --git a/src/lib/dhcp/iface_mgr_error_handler.h b/src/lib/dhcp/iface_mgr_error_handler.h index b0ea26b32c..5d97a21db4 100644 --- a/src/lib/dhcp/iface_mgr_error_handler.h +++ b/src/lib/dhcp/iface_mgr_error_handler.h @@ -29,15 +29,20 @@ /// @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); \ } \ } \ diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc index a30e154c45..4821da64b6 100644 --- a/src/lib/dhcp/iface_mgr_linux.cc +++ b/src/lib/dhcp/iface_mgr_linux.cc @@ -563,7 +563,7 @@ IfaceMgr::openMulticastSocket(Iface& iface, 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()); @@ -591,7 +591,7 @@ IfaceMgr::openMulticastSocket(Iface& iface, // 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()); diff --git a/src/lib/dhcp/iface_mgr_sun.cc b/src/lib/dhcp/iface_mgr_sun.cc index 2127faa60c..56b36e027f 100644 --- a/src/lib/dhcp/iface_mgr_sun.cc +++ b/src/lib/dhcp/iface_mgr_sun.cc @@ -157,7 +157,7 @@ IfaceMgr::openMulticastSocket(Iface& iface, 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()); diff --git a/src/lib/dhcpsrv/cfg_iface.cc b/src/lib/dhcpsrv/cfg_iface.cc index e1b88954f4..e129a4f482 100644 --- a/src/lib/dhcpsrv/cfg_iface.cc +++ b/src/lib/dhcpsrv/cfg_iface.cc @@ -59,7 +59,7 @@ CfgIface::multipleAddressesPerInterfaceActive() { 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(); @@ -169,8 +169,8 @@ CfgIface::openSockets(const uint16_t family, const uint16_t port, 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 diff --git a/src/lib/dhcpsrv/cfg_iface.h b/src/lib/dhcpsrv/cfg_iface.h index 5ae3dedf7b..2ee84432d0 100644 --- a/src/lib/dhcpsrv/cfg_iface.h +++ b/src/lib/dhcpsrv/cfg_iface.h @@ -182,7 +182,7 @@ public: /// 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. /// @@ -345,6 +345,13 @@ public: 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 OpenSocketsFailedCallback; @@ -437,12 +444,14 @@ private: /// 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, @@ -483,6 +492,9 @@ private: /// @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 .