]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2434] add socket status in response to status-get
authorAndrei Pavel <andrei@isc.org>
Thu, 21 Jul 2022 10:08:09 +0000 (13:08 +0300)
committerAndrei Pavel <andrei@isc.org>
Fri, 22 Jul 2022 11:36:31 +0000 (14:36 +0300)
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/lib/dhcp/iface_mgr.cc
src/lib/dhcp/iface_mgr.h
src/lib/dhcp/iface_mgr_bsd.cc
src/lib/dhcp/iface_mgr_error_handler.h
src/lib/dhcp/iface_mgr_linux.cc
src/lib/dhcp/iface_mgr_sun.cc
src/lib/dhcpsrv/cfg_iface.cc
src/lib/dhcpsrv/cfg_iface.h

index 431c13026fe3e24cf91f1fa7f84bc442eeaa7d62..6b62f0af02d3992440cc27a2538555b0bb62189d 100644 (file)
@@ -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));
 }
 
index 316937ebad4ff8b55fcf63a4a23c1e380f5b200b..28c59ec21e498b1e33bcb88c4e4d55d427d061ac 100644 (file)
@@ -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));
 }
 
index 9a346fbf8f1bb620e8c751c456c21d95a7bde462..abb0f27d6f3b666550787ab79239142ef132fb33 100644 (file)
@@ -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
index e4380291ea09bacf59c23bf74f5c0585c7061374..1e52bfdb9e4edade587167c57a8e30e8b03776e9 100644 (file)
@@ -138,6 +138,12 @@ public:
     /// @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.
@@ -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<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.
@@ -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
index 9b6cc9de339c2d295126ec8b0113c1ab9ffa8019..bc77d967f1f7a9bc481f96a5f5edf722e350a0b4 100644 (file)
@@ -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());
index b0ea26b32c16a8a889d2ba9ed73786ddc871dd27..5d97a21db494e26c43389142caf3aa01b3ad2814 100644 (file)
 /// @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); \
     } \
 } \
 
index a30e154c45c56a54658752f8c3f2a3a123aa4a1d..4821da64b6058db10ede0316d41623be66387ea8 100644 (file)
@@ -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());
index 2127faa60c90bf78ae1bd4eedeefa9023175e845..56b36e027fca7887c8ee5e8059e3fb6e853a135f 100644 (file)
@@ -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());
index e1b88954f47df7987c1811629f8a0ff4e4b2b5a9..e129a4f482086cb131ea68ab5c37d4f6e17191fd 100644 (file)
@@ -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
index 5ae3dedf7b17c3ec94bd34447165e822d8802b65..2ee84432d07f97abf22f1b4f5056c3e48ed821b5 100644 (file)
@@ -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<void(util::ReconnectCtlPtr)> 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 .