From e15dcdb37cea96b27eebac3eb7eebeab16b548f0 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 22 Sep 2022 10:34:31 +0200 Subject: [PATCH] [#2408] Improved handling rejected leases The new communication state functions are now MT safe. The rejected leases have also expiration time attached. --- .../high_availability/communication_state.cc | 91 ++++++++--- .../high_availability/communication_state.h | 144 +++++++++++++++--- .../tests/communication_state_unittest.cc | 83 +++++++++- .../dhcp/high_availability/tests/ha_test.h | 1 + 4 files changed, 273 insertions(+), 46 deletions(-) diff --git a/src/hooks/dhcp/high_availability/communication_state.cc b/src/hooks/dhcp/high_availability/communication_state.cc index db870ed444..76a15e9447 100644 --- a/src/hooks/dhcp/high_availability/communication_state.cc +++ b/src/hooks/dhcp/high_availability/communication_state.cc @@ -22,6 +22,7 @@ #include +#include #include #include #include @@ -324,6 +325,47 @@ CommunicationState::getAnalyzedMessagesCount() const { return (analyzed_messages_count_); } +size_t +CommunicationState::getRejectedLeaseUpdatesCount() { + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard lk(*mutex_); + return (getRejectedLeaseUpdatesCountInternal()); + } else { + return (getRejectedLeaseUpdatesCountInternal()); + } +} + +bool +CommunicationState::reportRejectedLeaseUpdate(const PktPtr& message, + const uint32_t lifetime) { + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard lk(*mutex_); + return (reportRejectedLeaseUpdateInternal(message, lifetime)); + } else { + return (reportRejectedLeaseUpdateInternal(message, lifetime)); + } +} + +bool +CommunicationState::reportSuccessfulLeaseUpdate(const PktPtr& message) { + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard lk(*mutex_); + return (reportSuccessfulLeaseUpdateInternal(message)); + } else { + return (reportSuccessfulLeaseUpdateInternal(message)); + } +} + +void +CommunicationState::clearRejectedLeaseUpdates() { + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard lk(*mutex_); + return (clearRejectedLeaseUpdatesInternal()); + } else { + return (clearRejectedLeaseUpdatesInternal()); + } +} + bool CommunicationState::clockSkewShouldWarn() { if (MultiThreadingMgr::instance().getMode()) { @@ -364,7 +406,7 @@ CommunicationState::clockSkewShouldWarnInternal() { } bool -CommunicationState::clockSkewShouldTerminate() const { +CommunicationState::clockSkewShouldTerminate() { if (MultiThreadingMgr::instance().getMode()) { std::lock_guard lk(*mutex_); // Issue a warning if the clock skew is greater than 60s. @@ -375,7 +417,7 @@ CommunicationState::clockSkewShouldTerminate() const { } bool -CommunicationState::clockSkewShouldTerminateInternal() const { +CommunicationState::clockSkewShouldTerminateInternal() { if (isClockSkewGreater(TERM_CLOCK_SKEW)) { LOG_ERROR(ha_logger, HA_HIGH_CLOCK_SKEW_CAUSES_TERMINATION) .arg(logFormatClockSkewInternal()); @@ -385,7 +427,7 @@ CommunicationState::clockSkewShouldTerminateInternal() const { } bool -CommunicationState::rejectedLeaseUpdatesShouldTerminate() const { +CommunicationState::rejectedLeaseUpdatesShouldTerminate() { if (MultiThreadingMgr::instance().getMode()) { std::lock_guard lk(*mutex_); return (rejectedLeaseUpdatesShouldTerminateInternal()); @@ -395,9 +437,9 @@ CommunicationState::rejectedLeaseUpdatesShouldTerminate() const { } bool -CommunicationState::rejectedLeaseUpdatesShouldTerminateInternal() const { +CommunicationState::rejectedLeaseUpdatesShouldTerminateInternal() { if (config_->getMaxRejectedLeaseUpdates() && - (config_->getMaxRejectedLeaseUpdates() <= getRejectedLeaseUpdatesCount())) { + (config_->getMaxRejectedLeaseUpdates() <= getRejectedLeaseUpdatesCountInternal())) { LOG_ERROR(ha_logger, HA_REJECTED_LEASE_UPDATES_CAUSE_TERMINATION); return (true); } @@ -701,28 +743,33 @@ CommunicationState4::clearConnectingClients() { } size_t -CommunicationState4::getRejectedLeaseUpdatesCount() const { - return (rejected_clients_.size()); +CommunicationState4::getRejectedLeaseUpdatesCountInternal() { + return (getRejectedLeaseUpdatesCountFromContainer(rejected_clients_)); } bool -CommunicationState4::reportRejectedLeaseUpdate(const PktPtr& message) { +CommunicationState4::reportRejectedLeaseUpdateInternal(const PktPtr& message, const uint32_t lifetime) { Pkt4Ptr msg = boost::dynamic_pointer_cast(message); if (!msg) { isc_throw(BadValue, "DHCP message for which the lease update was rejected is not a DHCPv4 message"); } auto client_id = getClientId(message, DHO_DHCP_CLIENT_IDENTIFIER); + RejectedClient4 client{ msg->getHWAddr()->hwaddr_, client_id, time(NULL) + lifetime }; auto existing_client = rejected_clients_.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id)); if (existing_client == rejected_clients_.end()) { - RejectedClient4 new_client{ msg->getHWAddr()->hwaddr_, client_id }; - rejected_clients_.insert(new_client); + rejected_clients_.insert(client); return (true); } + rejected_clients_.replace(existing_client, client); return (false); } bool -CommunicationState4::reportSuccessfulLeaseUpdate(const PktPtr& message) { +CommunicationState4::reportSuccessfulLeaseUpdateInternal(const PktPtr& message) { + // Early check if there is anything to do. + if (getRejectedLeaseUpdatesCountInternal() == 0) { + return (false); + } Pkt4Ptr msg = boost::dynamic_pointer_cast(message); if (!msg) { isc_throw(BadValue, "DHCP message for which the lease update was successful is not a DHCPv4 message"); @@ -737,7 +784,7 @@ CommunicationState4::reportSuccessfulLeaseUpdate(const PktPtr& message) { } void -CommunicationState4::clearRejectedLeaseUpdates() { +CommunicationState4::clearRejectedLeaseUpdatesInternal() { rejected_clients_.clear(); } @@ -869,12 +916,12 @@ CommunicationState6::clearConnectingClients() { } size_t -CommunicationState6::getRejectedLeaseUpdatesCount() const { - return (rejected_clients_.size()); +CommunicationState6::getRejectedLeaseUpdatesCountInternal() { + return (getRejectedLeaseUpdatesCountFromContainer(rejected_clients_)); } bool -CommunicationState6::reportRejectedLeaseUpdate(const PktPtr& message) { +CommunicationState6::reportRejectedLeaseUpdateInternal(const PktPtr& message, const uint32_t lifetime) { Pkt6Ptr msg = boost::dynamic_pointer_cast(message); if (!msg) { isc_throw(BadValue, "DHCP message for which the lease update was rejected is not a DHCPv6 message"); @@ -883,17 +930,22 @@ CommunicationState6::reportRejectedLeaseUpdate(const PktPtr& message) { if (duid.empty()) { return (false); } + RejectedClient6 client{ duid, time(NULL) + lifetime }; auto existing_client = rejected_clients_.find(duid); if (existing_client == rejected_clients_.end()) { - RejectedClient6 new_client{ duid }; - rejected_clients_.insert(new_client); + rejected_clients_.insert(client); return (true); } + rejected_clients_.replace(existing_client, client); return (false); } bool -CommunicationState6::reportSuccessfulLeaseUpdate(const PktPtr& message) { +CommunicationState6::reportSuccessfulLeaseUpdateInternal(const PktPtr& message) { + // Early check if there is anything to do. + if (getRejectedLeaseUpdatesCountInternal() == 0) { + return (false); + } Pkt6Ptr msg = boost::dynamic_pointer_cast(message); if (!msg) { isc_throw(BadValue, "DHCP message for which the lease update was successful is not a DHCPv6 message"); @@ -911,10 +963,9 @@ CommunicationState6::reportSuccessfulLeaseUpdate(const PktPtr& message) { } void -CommunicationState6::clearRejectedLeaseUpdates() { +CommunicationState6::clearRejectedLeaseUpdatesInternal() { rejected_clients_.clear(); } - } // end of namespace isc::ha } // end of namespace isc diff --git a/src/hooks/dhcp/high_availability/communication_state.h b/src/hooks/dhcp/high_availability/communication_state.h index ff0eea1859..a937025748 100644 --- a/src/hooks/dhcp/high_availability/communication_state.h +++ b/src/hooks/dhcp/high_availability/communication_state.h @@ -299,13 +299,65 @@ protected: public: + /// @brief Returns the number of lease updates rejected by the partner (MT safe). + /// + /// Each rejected lease update is counted only once if it failed + /// multiple times. Before returning the counter, it discards expired + /// rejected lease updates. + /// + /// @return Current rejected lease update number count. + size_t getRejectedLeaseUpdatesCount(); + +protected: + /// @brief Returns the number of lease updates rejected by the partner. /// /// Each rejected lease update is counted only once if it failed - /// multiple times + /// multiple times. Before returning the counter, it discards expired + /// rejected lease updates. /// /// @return Current rejected lease update number count. - virtual size_t getRejectedLeaseUpdatesCount() const = 0; + virtual size_t getRejectedLeaseUpdatesCountInternal() = 0; + + /// @brief Extracts the number of lease updates rejected by the partner + /// from the specified container. + /// + /// @param rejected_clients container holding rejected clients (v4 or v6). + /// @tparam RejectedClientsType type of the container holding rejected + /// clients. + /// @return Current rejected lease update number count. + template + static size_t getRejectedLeaseUpdatesCountFromContainer(RejectedClientsType& rejected_clients) { + if (rejected_clients.empty()) { + return (0); + } + auto& idx = rejected_clients.template get<1>(); + auto upper_limit = idx.upper_bound(time(NULL)); + if (upper_limit != idx.end()) { + auto lower_limit = idx.cbegin(); + idx.erase(lower_limit, upper_limit); + } + return (rejected_clients.size()); + } + +public: + + /// @brief Marks that the lease update failed due to a conflict for the + /// specified DHCP message (MT safe). + /// + /// If the conflict has been already reported for the given client, the + /// rejected lease count remains unchanged. + /// + /// @param message DHCP message for which a lease update failed due to + /// a conflict. + /// @param lifetime a time in seconds after which the rejected lease + /// update entry should be discarded. + /// @return true if the update was rejected for the first time, false + /// otherwise. + bool reportRejectedLeaseUpdate(const dhcp::PktPtr& message, + const uint32_t lifetime = 86400); + +protected: /// @brief Marks that the lease update failed due to a conflict for the /// specified DHCP message. @@ -315,9 +367,26 @@ public: /// /// @param message DHCP message for which a lease update failed due to /// a conflict. + /// @param lifetime a time in seconds after which the rejected lease + /// update entry should be discarded. /// @return true if the update was rejected for the first time, false /// otherwise. - virtual bool reportRejectedLeaseUpdate(const dhcp::PktPtr& message) = 0; + virtual bool reportRejectedLeaseUpdateInternal(const dhcp::PktPtr& message, + const uint32_t lifetime) = 0; +public: + + /// @brief Marks the lease update successful (MT safe). + /// + /// If the lease update was previously marked "in conflict", it is + /// now cleared, effectively lowering the number of conflicted leases. + /// + /// @param message DHCP message for which the lease update was + /// successful. + /// @return true when the lease was marked "in conflict" and it is + /// now cleared. + bool reportSuccessfulLeaseUpdate(const dhcp::PktPtr& message); + +protected: /// @brief Marks the lease update successful. /// @@ -328,10 +397,19 @@ public: /// successful. /// @return true when the lease was marked "in conflict" and it is /// now cleared. - virtual bool reportSuccessfulLeaseUpdate(const dhcp::PktPtr& message) = 0; + virtual bool reportSuccessfulLeaseUpdateInternal(const dhcp::PktPtr& message) = 0; + +public: + + /// @brief Clears rejected client leases (MT safe). + void clearRejectedLeaseUpdates(); + +protected: /// @brief Clears rejected client leases. - virtual void clearRejectedLeaseUpdates() = 0; + virtual void clearRejectedLeaseUpdatesInternal() = 0; + +public: /// @brief Issues a warning about high clock skew between the active /// servers if one is warranted. @@ -399,7 +477,7 @@ public: /// 60 seconds. In the future it may become configurable. /// /// @return true if the HA service should enter "terminated" state. - bool clockSkewShouldTerminate() const; + bool clockSkewShouldTerminate(); private: /// @brief Indicates whether the HA service should enter "terminated" @@ -418,7 +496,7 @@ private: /// 60 seconds. In the future it may become configurable. /// /// @return true if the HA service should enter "terminated" state. - bool clockSkewShouldTerminateInternal() const; + bool clockSkewShouldTerminateInternal(); /// @brief Checks if the clock skew is greater than the specified number /// of seconds. @@ -437,7 +515,7 @@ public: /// exceeds the value of max-rejected-lease-updates, false when the /// max-rejected-lease-updates is 0 or is greater than the current /// number of rejected lease updates. - bool rejectedLeaseUpdatesShouldTerminate() const; + bool rejectedLeaseUpdatesShouldTerminate(); private: @@ -448,7 +526,7 @@ private: /// exceeds the value of max-rejected-lease-updates, false when the /// max-rejected-lease-updates is 0 or is greater than the current /// number of rejected lease updates. - bool rejectedLeaseUpdatesShouldTerminateInternal() const; + bool rejectedLeaseUpdatesShouldTerminateInternal(); public: @@ -716,13 +794,16 @@ public: /// @return Number of unacked clients. virtual size_t getUnackedClientsCount() const; +protected: + /// @brief Returns the number of lease updates rejected by the partner. /// /// Each rejected lease update is counted only once if it failed - /// multiple times + /// multiple times. Before returning the counter, it discards expired + /// rejected lease updates. /// /// @return Current rejected lease update number count. - virtual size_t getRejectedLeaseUpdatesCount() const; + virtual size_t getRejectedLeaseUpdatesCountInternal(); /// @brief Marks that the lease update failed due to a conflict for the /// specified DHCP message. @@ -732,9 +813,12 @@ public: /// /// @param message DHCP message for which a lease update failed due to /// a conflict. + /// @param lifetime a time in seconds after which the rejected lease + /// update entry should be discarded. /// @return true if the update was rejected for the first time, false /// otherwise. - virtual bool reportRejectedLeaseUpdate(const dhcp::PktPtr& message); + virtual bool reportRejectedLeaseUpdateInternal(const dhcp::PktPtr& message, + const uint32_t lifetime); /// @brief Marks the lease update successful. /// @@ -745,12 +829,10 @@ public: /// successful. /// @return true when the lease was marked "in conflict" and it is /// now cleared. - virtual bool reportSuccessfulLeaseUpdate(const dhcp::PktPtr& message); + virtual bool reportSuccessfulLeaseUpdateInternal(const dhcp::PktPtr& message); /// @brief Clears rejected client leases. - virtual void clearRejectedLeaseUpdates(); - -protected: + virtual void clearRejectedLeaseUpdatesInternal(); /// @brief Checks if the DHCPv4 message appears to be unanswered. /// @@ -835,6 +917,7 @@ protected: struct RejectedClient4 { std::vector hwaddr_; std::vector clientid_; + int64_t expire_; }; /// @brief Multi index container holding information about the clients @@ -842,7 +925,11 @@ protected: typedef boost::multi_index_container< RejectedClient4, boost::multi_index::indexed_by< - ClientIdent4 + ClientIdent4, + boost::multi_index::ordered_non_unique< + boost::multi_index::member + > > > RejectedClients4; @@ -906,13 +993,16 @@ public: /// @return Number of unacked clients. virtual size_t getUnackedClientsCount() const; +protected: + /// @brief Returns the number of lease updates rejected by the partner. /// /// Each rejected lease update is counted only once if it failed - /// multiple times + /// multiple times. Before returning the counter, it discards expired + /// rejected lease updates. /// /// @return Current rejected lease update number count. - virtual size_t getRejectedLeaseUpdatesCount() const; + virtual size_t getRejectedLeaseUpdatesCountInternal(); /// @brief Marks that the lease update failed due to a conflict for the /// specified DHCP message. @@ -922,9 +1012,12 @@ public: /// /// @param message DHCP message for which a lease update failed due to /// a conflict. + /// @param lifetime a time in seconds after which the rejected lease + /// update entry should be discarded. /// @return true if the update was rejected for the first time, false /// otherwise. - virtual bool reportRejectedLeaseUpdate(const dhcp::PktPtr& message); + virtual bool reportRejectedLeaseUpdateInternal(const dhcp::PktPtr& message, + const uint32_t lifetime = 86400); /// @brief Marks the lease update successful. /// @@ -935,10 +1028,10 @@ public: /// successful. /// @return true when the lease was marked "in conflict" and it is /// now cleared. - virtual bool reportSuccessfulLeaseUpdate(const dhcp::PktPtr& message); + virtual bool reportSuccessfulLeaseUpdateInternal(const dhcp::PktPtr& message); /// @brief Clears rejected client leases. - virtual void clearRejectedLeaseUpdates(); + virtual void clearRejectedLeaseUpdatesInternal(); protected: @@ -1011,6 +1104,7 @@ protected: /// rejected lease update. struct RejectedClient6 { std::vector duid_; + int64_t expire_; }; /// @brief Multi index container holding information about the clients @@ -1018,7 +1112,11 @@ protected: typedef boost::multi_index_container< RejectedClient6, boost::multi_index::indexed_by< - ClientIdent6 + ClientIdent6, + boost::multi_index::ordered_non_unique< + boost::multi_index::member + > > > RejectedClients6; diff --git a/src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc b/src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc index 77f3227a75..e9e282b26f 100644 --- a/src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc @@ -137,6 +137,10 @@ public: /// rejected leases. void reportRejectedLeasesV6InvalidValuesTest(); + /// @brief Test that old rejected lease updates are discarded while + /// getting the rejected lease updates count. + void getRejectedLeaseUpdatesCountFromContainerTest(); + /// @brief Returns test heartbeat implementation. /// /// @return Pointer to heartbeat implementation function under test. @@ -729,15 +733,24 @@ void CommunicationStateTest::reportRejectedLeasesV4Test() { // Initially, there should be no rejected leases. EXPECT_EQ(0, state_.getRejectedLeaseUpdatesCount()); + // Reject lease update. auto msg = createMessage4(DHCPREQUEST, 1, 0, 0); state_.reportRejectedLeaseUpdate(msg); EXPECT_EQ(1, state_.getRejectedLeaseUpdatesCount()); + // Reject another lease update. msg = createMessage4(DHCPREQUEST, 2, 0, 0); state_.reportRejectedLeaseUpdate(msg); EXPECT_EQ(2, state_.getRejectedLeaseUpdatesCount()); + // Reject a lease with a short (zero) lease lifetime. + // This lease should be discarded when we call the + // getRejectedLeaseUpdatesCount(). + msg = createMessage4(DHCPREQUEST, 3, 0, 0); + state_.reportRejectedLeaseUpdate(msg, 0); + EXPECT_EQ(2, state_.getRejectedLeaseUpdatesCount()); + // Reject lease update for a client using the same MAC // address but different client identifier. It should // be treated as a different lease. @@ -779,6 +792,9 @@ CommunicationStateTest::reportSuccessfulLeasesV4Test() { void CommunicationStateTest::reportRejectedLeasesV4InvalidValuesTest() { + // Populate one valid update. Without it our functions under test + // would return early. + state_.reportRejectedLeaseUpdate(createMessage4(DHCPREQUEST, 1, 0, 0)); // Using DHCPv6 message in the DHCPv4 context is a programming // error and deserves an exception. auto msg = createMessage6(DHCPV6_REQUEST, 1, 0); @@ -790,18 +806,29 @@ void CommunicationStateTest::reportRejectedLeasesV6Test() { // Initially, there should be no rejected leases. EXPECT_EQ(0, state6_.getRejectedLeaseUpdatesCount()); + // Reject lease update. - auto msg = createMessage6(DHCPV6_SOLICIT, 1, 0); + auto msg = createMessage6(DHCPV6_REQUEST, 1, 0); state6_.reportRejectedLeaseUpdate(msg); EXPECT_EQ(1, state6_.getRejectedLeaseUpdatesCount()); + // Reject another lease update. - msg = createMessage6(DHCPV6_SOLICIT, 2, 0); + msg = createMessage6(DHCPV6_REQUEST, 2, 0); state6_.reportRejectedLeaseUpdate(msg); EXPECT_EQ(2, state6_.getRejectedLeaseUpdatesCount()); + + // Reject a lease with a short (zero) lease lifetime. + // This lease should be discarded when we call the + // getRejectedLeaseUpdatesCount(). + msg = createMessage6(DHCPV6_REQUEST, 3, 0); + state6_.reportRejectedLeaseUpdate(msg, 0); + EXPECT_EQ(2, state6_.getRejectedLeaseUpdatesCount()); + // Reject it again. It should not affect the counter. - msg = createMessage6(DHCPV6_SOLICIT, 2, 0); + msg = createMessage6(DHCPV6_REQUEST, 2, 0); state6_.reportRejectedLeaseUpdate(msg); EXPECT_EQ(2, state6_.getRejectedLeaseUpdatesCount()); + // Clear rejected lease updates and make sure they // are now 0. state6_.clearRejectedLeaseUpdates(); @@ -838,6 +865,9 @@ CommunicationStateTest::reportSuccessfulLeasesV6Test() { void CommunicationStateTest::reportRejectedLeasesV6InvalidValuesTest() { + // Populate one valid update. Without it our functions under test + // would return early. + state6_.reportRejectedLeaseUpdate(createMessage6(DHCPV6_REQUEST, 1, 0)); // Using DHCPv4 message in the DHCPv6 context is a programming // error and deserves an exception. auto msg0 = createMessage4(DHCPREQUEST, 1, 1, 0); @@ -850,6 +880,49 @@ CommunicationStateTest::reportRejectedLeasesV6InvalidValuesTest() { EXPECT_FALSE(state6_.reportSuccessfulLeaseUpdate(msg1)); } +void +CommunicationStateTest::getRejectedLeaseUpdatesCountFromContainerTest() { + // Create a simple multi index container with two indexes. The + // first index is on the ordinal number to distinguish between + // different entries. The second index is on the expire_ field + // that is identical to the expire_ field in the RejectedClients4 + // and RejectedClients6 containers. + struct Entry { + int64_t ordinal_; + int64_t expire_; + }; + typedef boost::multi_index_container< + Entry, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::member + >, + boost::multi_index::ordered_non_unique< + boost::multi_index::member + > + > + > Entries; + // Add many entries to the container. Odd entries have lifetime + // expiring in the future. Even entries have lifetimes expiring in + // the past. + Entries entries; + for (auto i = 0; i < 1000; i++) { + entries.insert({i, time(NULL) + (i % 2 ? 100 + i : -1 - i)}); + } + // Get the count of valid entries. It should remove the expiring + // entries. + auto valid_entries_count = state_.getRejectedLeaseUpdatesCountFromContainer(entries); + EXPECT_EQ(500, valid_entries_count); + EXPECT_EQ(500, entries.size()); + + // Validate that we removed expired entries, not the valid ones. + for (auto entry : entries) { + EXPECT_EQ(1, entry.ordinal_ % 2); + } +} + TEST_F(CommunicationStateTest, partnerStateTest) { partnerStateTest(); } @@ -1057,4 +1130,8 @@ TEST_F(CommunicationStateTest, reportRejectedLeasesV6InvalidValuesTestMultiThrea reportRejectedLeasesV6InvalidValuesTest(); } +TEST_F(CommunicationStateTest, getRejectedLeaseUpdatesCountFromContainerTest) { + getRejectedLeaseUpdatesCountFromContainerTest(); +} + } diff --git a/src/hooks/dhcp/high_availability/tests/ha_test.h b/src/hooks/dhcp/high_availability/tests/ha_test.h index a98646727e..0b763cabe9 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_test.h +++ b/src/hooks/dhcp/high_availability/tests/ha_test.h @@ -60,6 +60,7 @@ public: using StateType::my_time_at_skew_; using StateType::partner_time_at_skew_; using StateType::unsent_update_count_; + using StateType::getRejectedLeaseUpdatesCountFromContainer; }; /// @brief Type of the NakedCommunicationState for DHCPv4.