]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1087] Refactored tracking unacked clients
authorMarcin Siodelski <marcin@isc.org>
Fri, 15 May 2020 11:51:03 +0000 (13:51 +0200)
committerMarcin Siodelski <marcin@isc.org>
Wed, 20 May 2020 15:48:16 +0000 (17:48 +0200)
The information about the unacked clients was now extended to track all
clients trying to connect, even when they are not consiered unacked yet.
The new data structures use multi index container rather than
set/multimap.

src/hooks/dhcp/high_availability/communication_state.cc
src/hooks/dhcp/high_availability/communication_state.h

index 049c61a414276ec9f97fa961ef0b8cabf20ff736..b4d1d096c48fb73900d545bf608f2e56335ff3bd 100644 (file)
@@ -155,10 +155,10 @@ CommunicationState::poke() {
     // Set poke time to the current time.
     poke_time_ = boost::posix_time::microsec_clock::universal_time();
 
-    // If we have been tracking the unanswered DHCP messages directed to the
-    // partner, we need to clear any gathered information because the connection
+    // If we have been tracking the DHCP messages directed to the partner,
+    // we need to clear any gathered information because the connection
     // seems to be (re)established.
-    clearUnackedClients();
+    clearConnectingClients();
     analyzed_messages_count_ = 0;
 
     if (timer_) {
@@ -271,7 +271,7 @@ CommunicationState::logFormatClockSkew() const {
 
 CommunicationState4::CommunicationState4(const IOServicePtr& io_service,
                                          const HAConfigPtr& config)
-    : CommunicationState(io_service, config), unacked_clients_() {
+    : CommunicationState(io_service, config), connecting_clients_() {
 }
 
 void
@@ -295,17 +295,11 @@ CommunicationState4::analyzeMessage(const boost::shared_ptr<dhcp::Pkt>& message)
         secs = ((secs >> 8) | (secs << 8));
     }
 
-    // Check the value of the "secs" field. If it is below the threshold there
-    // is nothing to do. The "secs" field holds a value in seconds, hence we
-    // have to multiple by 1000 to get a value in milliseconds.
-    if (secs * 1000 <= config_->getMaxAckDelay()) {
-        return;
-    }
-
-    // The "secs" value is above the threshold so we should count it as unacked
-    // request, but we will first have to check if there is such request already
-    // recorded.
-    auto existing_requests = unacked_clients_.equal_range(msg->getHWAddr()->hwaddr_);
+    // Check the value of the "secs" field. The "secs" field holds a value in
+    // seconds, hence we have to multiple by 1000 to get a value in milliseconds.
+    // If the secs value is above the threshold, it means that the current
+    // client should be considered unacked.
+    auto unacked = (secs * 1000 > config_->getMaxAckDelay());
 
     // Client identifier will be stored together with the hardware address. It
     // may remain empty if the client hasn't specified it.
@@ -315,39 +309,46 @@ CommunicationState4::analyzeMessage(const boost::shared_ptr<dhcp::Pkt>& message)
         client_id = opt_client_id->getData();
     }
 
-    // Iterate over the requests we found so far and see if we have a match with
-    // the client identifier (this includes empty client identifiers).
-    for (auto r = existing_requests.first; r != existing_requests.second; ++r) {
-        if (r->second == client_id) {
-            // There is a match so we have already recorded this client as
-            // unacked.
-            return;
+    // Check if the given client was already recorded.
+    auto& idx = connecting_clients_.get<0>();
+    auto existing_request = idx.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id));
+    if (existing_request != idx.end()) {
+        // If the client was recorded but was not considered unacked
+        // but it should be considered unacked as a result of processing
+        // this packet, let's update the recorded request to mark the
+        // client unacked.
+        if (!existing_request->unacked_ && unacked) {
+            ConnectingClient4 connecting_client{ msg->getHWAddr()->hwaddr_, client_id, unacked };
+            idx.replace(existing_request, connecting_client);
         }
+        return;
     }
 
-    // New unacked client detected, so record the required information.
-    unacked_clients_.insert(std::make_pair(msg->getHWAddr()->hwaddr_, client_id));
+    // This is the first time we see the packet from this client. Let's
+    // record it.
+    ConnectingClient4 connecting_client{ msg->getHWAddr()->hwaddr_, client_id, unacked };
+    idx.insert(connecting_client);
 }
 
 bool
 CommunicationState4::failureDetected() const {
     return ((config_->getMaxUnackedClients() == 0) ||
-            (unacked_clients_.size() > config_->getMaxUnackedClients()));
+            (getUnackedClientsCount() > config_->getMaxUnackedClients()));
 }
 
 size_t
 CommunicationState4::getUnackedClientsCount() const {
-    return (unacked_clients_.size());
+    return (connecting_clients_.get<1>().count(true));
 }
 
 void
-CommunicationState4::clearUnackedClients() {
-    unacked_clients_.clear();
+CommunicationState4::clearConnectingClients() {
+    connecting_clients_.clear();
 }
 
 CommunicationState6::CommunicationState6(const IOServicePtr& io_service,
                                          const HAConfigPtr& config)
-    : CommunicationState(io_service, config), unacked_clients_() {
+    : CommunicationState(io_service, config), connecting_clients_() {
 }
 
 void
@@ -365,32 +366,48 @@ CommunicationState6::analyzeMessage(const boost::shared_ptr<dhcp::Pkt>& message)
     // 1/100 of second, hence we have to multiply by 10 to get a value in milliseconds.
     OptionUint16Ptr elapsed_time = boost::dynamic_pointer_cast<
         OptionUint16>(msg->getOption(D6O_ELAPSED_TIME));
-    if (!elapsed_time || elapsed_time->getValue() * 10 <= config_->getMaxAckDelay()) {
-        return;
-    }
+    auto unacked = (elapsed_time && elapsed_time->getValue() * 10 > config_->getMaxAckDelay());
 
     // Get the DUID of the client to see if it hasn't been recorded already.
     OptionPtr duid = msg->getOption(D6O_CLIENTID);
-    if (duid && unacked_clients_.count(duid->getData()) == 0) {
-        // New unacked client detected, so record the required information.
-        unacked_clients_.insert(duid->getData());
+    if (!duid) {
+        return;
     }
+
+    // Check if the given client was already recorded.
+    auto& idx = connecting_clients_.get<0>();
+    auto existing_request = idx.find(duid->getData());
+    if (existing_request != idx.end()) {
+        // If the client was recorded but was not considered unacked
+        // but it should be considered unacked as a result of processing
+        // this packet, let's update the recorded request to mark the
+        // client unacked.
+        if (!existing_request->unacked_ && unacked) {
+            ConnectingClient6 connecting_client{ duid->getData(), unacked };
+            idx.replace(existing_request, connecting_client);
+        }
+    }
+
+    // This is the first time we see the packet from this client. Let's
+    // record it.
+    ConnectingClient6 connecting_client{ duid->getData(), unacked };
+    idx.insert(connecting_client);
 }
 
 bool
 CommunicationState6::failureDetected() const {
     return ((config_->getMaxUnackedClients() == 0) ||
-            (unacked_clients_.size() > config_->getMaxUnackedClients()));
+            (getUnackedClientsCount() > config_->getMaxUnackedClients()));
 }
 
 size_t
 CommunicationState6::getUnackedClientsCount() const {
-    return (unacked_clients_.size());
+    return (connecting_clients_.get<1>().count(true));
 }
 
 void
-CommunicationState6::clearUnackedClients() {
-    unacked_clients_.clear();
+CommunicationState6::clearConnectingClients() {
+    connecting_clients_.clear();
 }
 
 } // end of namespace isc::ha
index 1acf690669eee871e9bfd730a7a67efe870d871d..cc396cfe58c13c70acdf8ea65b94b1cad9f7bd55 100644 (file)
 #include <dhcp/pkt.h>
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/function.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/composite_key.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/indexed_by.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/ordered_index.hpp>
 #include <boost/shared_ptr.hpp>
 #include <map>
 #include <set>
@@ -233,8 +239,9 @@ public:
 
 protected:
 
-    /// @brief Removes information about clients which the partner server
-    /// failed to respond to.
+    /// @brief Removes information about the clients the partner server
+    /// should respond to while communication with the partner was
+    /// interrupted.
     ///
     /// This information is cleared by the @c CommunicationState::poke.
     /// The derivations of this class must provide DHCPv4 and DHCPv6 specific
@@ -244,7 +251,7 @@ protected:
     /// procedure starts over.
     ///
     /// See @c CommunicationState::analyzeMessage for details.
-    virtual void clearUnackedClients() = 0;
+    virtual void clearConnectingClients() = 0;
 
 public:
 
@@ -417,19 +424,49 @@ public:
 
 protected:
 
-    /// @brief Removes information about clients which the partner server
-    /// failed to respond to.
+    /// @brief Removes information about the clients the partner server
+    /// should respond to while communication with the partner was
+    /// interrupted.
     ///
     /// See @c CommunicationState::analyzeMessage for details.
-    virtual void clearUnackedClients();
-
-    /// @brief Holds information about the clients which the partner server
-    /// failed to respond to.
-    ///
-    /// The key of the multimap holds hardware addresses of the clients.
-    /// The value of the multimap holds client identifiers of the
-    /// clients. The client identifiers may be empty.
-    std::multimap<std::vector<uint8_t>, std::vector<uint8_t> > unacked_clients_;
+    virtual void clearConnectingClients();
+
+    /// @brief Structure holding information about the client which has
+    /// send the packet being analyzed.
+    struct ConnectingClient4 {
+        std::vector<uint8_t> hwaddr_;
+        std::vector<uint8_t> clientid_;
+        bool unacked_;
+    };
+
+    /// @brief Multi index container holding information about the clients
+    /// attempting to get leases from the partner server.
+    typedef boost::multi_index_container<
+        ConnectingClient4,
+        boost::multi_index::indexed_by<
+            // First index is a composite index which allows to find a client
+            // by the HW address/client identifier tuple.
+            boost::multi_index::hashed_unique<
+                boost::multi_index::composite_key<
+                    ConnectingClient4,
+                    boost::multi_index::member<ConnectingClient4, std::vector<uint8_t>,
+                                               &ConnectingClient4::hwaddr_>,
+                    boost::multi_index::member<ConnectingClient4, std::vector<uint8_t>,
+                                               &ConnectingClient4::clientid_>
+                >
+            >,
+            // Second index allows for counting all clients which are
+            // considered unacked.
+            boost::multi_index::ordered_non_unique<
+                boost::multi_index::member<ConnectingClient4, bool, &ConnectingClient4::unacked_>
+            >
+        >
+    > ConnectingClients4;
+
+    /// @brief Holds information about the clients attempting to contact
+    /// the partner server while the servers are in communications
+    /// interrupted state.
+    ConnectingClients4 connecting_clients_;
 };
 
 /// @brief Pointer to the @c CommunicationState4 object.
@@ -479,17 +516,42 @@ public:
 
 protected:
 
-    /// @brief Removes information about clients which the partner server
-    /// failed to respond to.
+    /// @brief Removes information about the clients the partner server
+    /// should respond to while communication with the partner was
+    /// interrupted.
     ///
     /// See @c CommunicationState::analyzeMessage for details.
-    virtual void clearUnackedClients();
-
-    /// @brief Holds information about the clients which the partner server
-    /// failed to respond to.
-    ///
-    /// The value of the set holds DUIDs of the clients.
-    std::set<std::vector<uint8_t> > unacked_clients_;
+    virtual void clearConnectingClients();
+
+    /// @brief Structure holding information about the client which has
+    /// send the packet being analyzed.
+    struct ConnectingClient6 {
+        std::vector<uint8_t> duid_;
+        bool unacked_;
+    };
+
+    /// @brief Multi index container holding information about the clients
+    /// attempting to get leases from the partner server.
+    typedef boost::multi_index_container<
+        ConnectingClient6,
+        boost::multi_index::indexed_by<
+            // First index is for accessing connecting clients by DUID.
+            boost::multi_index::hashed_unique<
+                boost::multi_index::member<ConnectingClient6, std::vector<uint8_t>,
+                                           &ConnectingClient6::duid_>
+            >,
+            // Second index allows for counting all clients which are
+            // considered unacked.
+            boost::multi_index::ordered_non_unique<
+                boost::multi_index::member<ConnectingClient6, bool, &ConnectingClient6::unacked_>
+            >
+        >
+    > ConnectingClients6;
+
+    /// @brief Holds information about the clients attempting to contact
+    /// the partner server while the servers are in communications
+    /// interrupted state.
+    ConnectingClients6 connecting_clients_;
 };
 
 /// @brief Pointer to the @c CommunicationState6 object.