23#include <boost/pointer_cast.hpp>
38using namespace boost::posix_time;
44constexpr long WARN_CLOCK_SKEW = 30;
47constexpr long TERM_CLOCK_SKEW = 60;
50constexpr long MIN_TIME_SINCE_CLOCK_SKEW_WARN = 60;
59 : io_service_(io_service), config_(config), timer_(), interval_(0),
60 poke_time_(
boost::posix_time::microsec_clock::universal_time()),
61 heartbeat_impl_(0), partner_state_(-1), partner_state_time_(),
62 partner_scopes_(), clock_skew_(0, 0, 0, 0), last_clock_skew_warn_(),
63 my_time_at_skew_(), partner_time_at_skew_(),
64 analyzed_messages_count_(0), unsent_update_count_(0),
65 partner_unsent_update_count_{0, 0}, mutex_(new mutex()) {
75 std::lock_guard<std::mutex> lk(*
mutex_);
76 poke_time_ += boost::posix_time::seconds(secs);
78 poke_time_ += boost::posix_time::seconds(secs);
85 std::lock_guard<std::mutex> lk(*
mutex_);
95 std::lock_guard<std::mutex> lk(*
mutex_);
96 setPartnerStateInternal(state);
98 setPartnerStateInternal(state);
105 std::lock_guard<std::mutex> lk(*
mutex_);
106 setPartnerStateInternal(
"unavailable");
107 resetPartnerTimeInternal();
109 setPartnerStateInternal(
"unavailable");
110 resetPartnerTimeInternal();
115CommunicationState::setPartnerStateInternal(
const std::string& state) {
119 setCurrentPartnerStateTimeInternal();
130 ptime now = boost::posix_time::microsec_clock::universal_time();
132 std::lock_guard<std::mutex> lk(*
mutex_);
140CommunicationState::setCurrentPartnerStateTimeInternal() {
147 std::lock_guard<std::mutex> lk(*
mutex_);
157 std::lock_guard<std::mutex> lk(*
mutex_);
158 setPartnerScopesInternal(new_scopes);
160 setPartnerScopesInternal(new_scopes);
165CommunicationState::setPartnerScopesInternal(
ConstElementPtr new_scopes) {
166 if (!new_scopes || (new_scopes->getType() !=
Element::list)) {
168 " the received value is not a valid JSON list");
171 std::set<std::string> partner_scopes;
172 for (
auto i = 0; i < new_scopes->size(); ++i) {
173 auto scope = new_scopes->get(i);
176 " the received scope value is not a valid JSON string");
178 auto scope_str = scope->stringValue();
179 if (!scope_str.empty()) {
180 partner_scopes.insert(scope_str);
188 const std::function<
void()>& heartbeat_impl) {
190 std::lock_guard<std::mutex> lk(*
mutex_);
191 startHeartbeatInternal(interval, heartbeat_impl);
193 startHeartbeatInternal(interval, heartbeat_impl);
198CommunicationState::startHeartbeatInternal(
const long interval,
199 const std::function<
void()>& heartbeat_impl) {
200 bool settings_modified =
false;
204 if (heartbeat_impl) {
205 settings_modified =
true;
212 " to the heartbeat implementation is not specified");
218 settings_modified |= (
interval_ != interval);
226 " for the heartbeat timer is not specified");
233 if (settings_modified) {
241 std::lock_guard<std::mutex> lk(*
mutex_);
242 stopHeartbeatInternal();
244 stopHeartbeatInternal();
249CommunicationState::stopHeartbeatInternal() {
261 std::lock_guard<std::mutex> lk(*
mutex_);
262 return (
static_cast<bool>(
timer_));
264 return (
static_cast<bool>(
timer_));
268boost::posix_time::time_duration
271 std::lock_guard<std::mutex> lk(*
mutex_);
272 return (updatePokeTimeInternal());
274 return (updatePokeTimeInternal());
278boost::posix_time::time_duration
279CommunicationState::updatePokeTimeInternal() {
281 boost::posix_time::ptime prev_poke_time =
poke_time_;
283 poke_time_ = boost::posix_time::microsec_clock::universal_time();
290 std::lock_guard<std::mutex> lk(*
mutex_);
298CommunicationState::pokeInternal() {
300 boost::posix_time::time_duration duration_since_poke = updatePokeTimeInternal();
313 if (duration_since_poke.total_seconds() > 0) {
317 startHeartbeatInternal();
325 std::lock_guard<std::mutex> lk(*
mutex_);
326 return (getDurationInMillisecsInternal());
328 return (getDurationInMillisecsInternal());
333CommunicationState::getDurationInMillisecsInternal()
const {
334 ptime now = boost::posix_time::microsec_clock::universal_time();
336 return (duration.total_milliseconds());
346 const uint16_t option_type) {
347 std::vector<uint8_t> client_id;
348 OptionPtr opt_client_id = message->getOption(option_type);
350 client_id = opt_client_id->getData();
363 std::lock_guard<std::mutex> lk(*
mutex_);
372 const uint32_t lifetime) {
374 std::lock_guard<std::mutex> lk(*
mutex_);
384 std::lock_guard<std::mutex> lk(*
mutex_);
394 std::lock_guard<std::mutex> lk(*
mutex_);
404 std::lock_guard<std::mutex> lk(*
mutex_);
405 return (clockSkewShouldWarnInternal());
407 return (clockSkewShouldWarnInternal());
412CommunicationState::clockSkewShouldWarnInternal() {
414 if (isClockSkewGreater(WARN_CLOCK_SKEW)) {
421 ptime now = boost::posix_time::microsec_clock::universal_time();
428 (since_warn_duration.total_seconds() > MIN_TIME_SINCE_CLOCK_SKEW_WARN)) {
431 .arg(
config_->getThisServerName())
432 .arg(logFormatClockSkewInternal());
444 std::lock_guard<std::mutex> lk(*
mutex_);
446 return (clockSkewShouldTerminateInternal());
448 return (clockSkewShouldTerminateInternal());
453CommunicationState::clockSkewShouldTerminateInternal() {
454 if (isClockSkewGreater(TERM_CLOCK_SKEW)) {
456 .arg(
config_->getThisServerName())
457 .arg(logFormatClockSkewInternal());
466 std::lock_guard<std::mutex> lk(*
mutex_);
467 return (rejectedLeaseUpdatesShouldTerminateInternal());
469 return (rejectedLeaseUpdatesShouldTerminateInternal());
474CommunicationState::rejectedLeaseUpdatesShouldTerminateInternal() {
475 if (
config_->getMaxRejectedLeaseUpdates() &&
478 .arg(
config_->getThisServerName());
485CommunicationState::isClockSkewGreater(
const long seconds)
const {
493 std::lock_guard<std::mutex> lk(*
mutex_);
494 setPartnerTimeInternal(time_text);
496 setPartnerTimeInternal(time_text);
501CommunicationState::setPartnerTimeInternal(
const std::string& time_text) {
508CommunicationState::resetPartnerTimeInternal() {
509 clock_skew_ = boost::posix_time::time_duration(0, 0, 0, 0);
518 std::lock_guard<std::mutex> lk(*
mutex_);
519 return (logFormatClockSkewInternal());
521 return (logFormatClockSkewInternal());
526CommunicationState::logFormatClockSkewInternal()
const {
527 std::ostringstream os;
533 return (
"skew not initialized");
540 <<
", partner's clock is ";
544 os <<
"synchroninzed";
547 os <<
clock_skew_.invert_sign().total_seconds() <<
"s behind";
577 report->set(
"last-scopes", list);
578 report->set(
"communication-interrupted",
583 long long unacked_clients_left = 0;
585 unacked_clients_left =
static_cast<long long>(
config_->getMaxUnackedClients() -
588 report->set(
"unacked-clients-left",
Element::create(unacked_clients_left));
604 std::lock_guard<std::mutex> lk(*
mutex_);
614 std::lock_guard<std::mutex> lk(*
mutex_);
615 increaseUnsentUpdateCountInternal();
617 increaseUnsentUpdateCountInternal();
622CommunicationState::increaseUnsentUpdateCountInternal() {
635 std::lock_guard<std::mutex> lk(*
mutex_);
636 return (hasPartnerNewUnsentUpdatesInternal());
638 return (hasPartnerNewUnsentUpdatesInternal());
643CommunicationState::hasPartnerNewUnsentUpdatesInternal()
const {
651 std::lock_guard<std::mutex> lk(*
mutex_);
652 setPartnerUnsentUpdateCountInternal(unsent_update_count);
654 setPartnerUnsentUpdateCountInternal(unsent_update_count);
659CommunicationState::setPartnerUnsentUpdateCountInternal(uint64_t unsent_update_count) {
664boost::posix_time::ptime
669boost::posix_time::ptime
677 rejected_clients_() {
683 std::lock_guard<std::mutex> lk(*
mutex_);
693 Pkt4Ptr msg = boost::dynamic_pointer_cast<Pkt4>(message);
702 uint16_t secs = msg->getSecs();
707 if ((secs > 255) && ((secs & 0xFF) == 0)) {
708 secs = ((secs >> 8) | (secs << 8));
715 auto unacked = (secs * 1000 >
config_->getMaxAckDelay());
719 auto client_id =
getClientId(message, DHO_DHCP_CLIENT_IDENTIFIER);
720 bool log_unacked =
false;
724 auto existing_request = idx.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id));
725 if (existing_request != idx.end()) {
730 if (!existing_request->unacked_ && unacked) {
732 idx.replace(existing_request, connecting_client);
740 idx.insert(connecting_client);
741 log_unacked = unacked;
748 .arg(
config_->getThisServerName())
749 .arg(message->getLabel());
755 unsigned unacked_left = 0;
757 if (
config_->getMaxUnackedClients() >= unacked_total) {
758 unacked_left =
config_->getMaxUnackedClients() - unacked_total + 1;
761 .arg(
config_->getThisServerName())
762 .arg(message->getLabel())
771 std::lock_guard<std::mutex> lk(*
mutex_);
780 return ((
config_->getMaxUnackedClients() == 0) ||
782 config_->getMaxUnackedClients()));
788 std::lock_guard<std::mutex> lk(*
mutex_);
798 std::lock_guard<std::mutex> lk(*
mutex_);
817 Pkt4Ptr msg = boost::dynamic_pointer_cast<Pkt4>(message);
819 isc_throw(
BadValue,
"DHCP message for which the lease update was rejected is not a DHCPv4 message");
821 auto client_id =
getClientId(message, DHO_DHCP_CLIENT_IDENTIFIER);
823 auto existing_client =
rejected_clients_.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id));
838 Pkt4Ptr msg = boost::dynamic_pointer_cast<Pkt4>(message);
840 isc_throw(
BadValue,
"DHCP message for which the lease update was successful is not a DHCPv4 message");
842 auto client_id =
getClientId(msg, DHO_DHCP_CLIENT_IDENTIFIER);
843 auto existing_client =
rejected_clients_.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id));
859 rejected_clients_() {
865 std::lock_guard<std::mutex> lk(*
mutex_);
875 Pkt6Ptr msg = boost::dynamic_pointer_cast<Pkt6>(message);
887 auto unacked = (elapsed_time && elapsed_time->getValue() * 10 >
config_->getMaxAckDelay());
895 bool log_unacked =
false;
899 auto existing_request = idx.find(duid);
900 if (existing_request != idx.end()) {
905 if (!existing_request->unacked_ && unacked) {
907 idx.replace(existing_request, connecting_client);
915 idx.insert(connecting_client);
916 log_unacked = unacked;
923 .arg(
config_->getThisServerName())
924 .arg(message->getLabel());
930 unsigned unacked_left = 0;
932 if (
config_->getMaxUnackedClients() >= unacked_total) {
933 unacked_left =
config_->getMaxUnackedClients() - unacked_total + 1;
936 .arg(
config_->getThisServerName())
937 .arg(message->getLabel())
946 std::lock_guard<std::mutex> lk(*
mutex_);
955 return ((
config_->getMaxUnackedClients() == 0) ||
957 config_->getMaxUnackedClients()));
963 std::lock_guard<std::mutex> lk(*
mutex_);
973 std::lock_guard<std::mutex> lk(*
mutex_);
992 Pkt6Ptr msg = boost::dynamic_pointer_cast<Pkt6>(message);
994 isc_throw(
BadValue,
"DHCP message for which the lease update was rejected is not a DHCPv6 message");
1016 Pkt6Ptr msg = boost::dynamic_pointer_cast<Pkt6>(message);
1018 isc_throw(
BadValue,
"DHCP message for which the lease update was successful is not a DHCPv6 message");
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
The IntervalTimer class is a wrapper for the ASIO boost::asio::deadline_timer class.
static ElementPtr create(const Position &pos=ZERO_POSITION())
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
virtual bool reportRejectedLeaseUpdateInternal(const dhcp::PktPtr &message, const uint32_t lifetime)
Marks that the lease update failed due to a conflict for the specified DHCP message.
virtual size_t getRejectedLeaseUpdatesCountInternal()
Returns the number of lease updates rejected by the partner.
virtual bool reportSuccessfulLeaseUpdateInternal(const dhcp::PktPtr &message)
Marks the lease update successful.
virtual size_t getUnackedClientsCount() const
Returns the current number of clients which haven't gotten a lease from the partner server.
virtual void clearRejectedLeaseUpdatesInternal()
Clears rejected client leases.
virtual void analyzeMessageInternal(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv4 message appears to be unanswered.
virtual size_t getConnectingClientsCount() const
Returns the current number of clients which attempted to get a lease from the partner server.
virtual void analyzeMessage(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv4 message appears to be unanswered.
RejectedClients4 rejected_clients_
Holds information about the clients for whom lease updates have been rejected by the partner.
virtual bool failureDetectedInternal() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
ConnectingClients4 connecting_clients_
Holds information about the clients attempting to contact the partner server while the servers are in...
virtual bool failureDetected() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
virtual void clearConnectingClients()
Removes information about the clients the partner server should respond to while communication with t...
CommunicationState4(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
virtual void analyzeMessage(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv6 message appears to be unanswered.
RejectedClients6 rejected_clients_
Holds information about the clients for whom lease updates have been rejected by the partner.
virtual size_t getRejectedLeaseUpdatesCountInternal()
Returns the number of lease updates rejected by the partner.
ConnectingClients6 connecting_clients_
Holds information about the clients attempting to contact the partner server while the servers are in...
CommunicationState6(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
virtual bool reportSuccessfulLeaseUpdateInternal(const dhcp::PktPtr &message)
Marks the lease update successful.
virtual void clearConnectingClients()
Removes information about the clients the partner server should respond to while communication with t...
virtual bool failureDetected() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
virtual size_t getUnackedClientsCount() const
Returns the current number of clients which haven't gotten a lease from the partner server.
virtual bool failureDetectedInternal() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
virtual void analyzeMessageInternal(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv6 message appears to be unanswered.
virtual bool reportRejectedLeaseUpdateInternal(const dhcp::PktPtr &message, const uint32_t lifetime=86400)
Marks that the lease update failed due to a conflict for the specified DHCP message.
virtual size_t getConnectingClientsCount() const
Returns the current number of clients which attempted to get a lease from the partner server.
virtual void clearRejectedLeaseUpdatesInternal()
Clears rejected client leases.
Holds communication state between the two HA peers.
virtual size_t getConnectingClientsCount() const =0
Returns the current number of clients which attempted to get a lease from the partner server.
virtual bool reportRejectedLeaseUpdateInternal(const dhcp::PktPtr &message, const uint32_t lifetime)=0
Marks that the lease update failed due to a conflict for the specified DHCP message.
boost::posix_time::ptime partner_state_time_
Holds a time when partner was first seen in the current state.
virtual void clearRejectedLeaseUpdatesInternal()=0
Clears rejected client leases.
virtual size_t getUnackedClientsCount() const =0
Returns the current number of clients which haven't got the lease from the partner server.
virtual void clearConnectingClients()=0
Removes information about the clients the partner server should respond to while communication with t...
void clearRejectedLeaseUpdates()
Clears rejected client leases (MT safe).
void startHeartbeat(const long interval, const std::function< void()> &heartbeat_impl)
Starts recurring heartbeat (public interface).
uint64_t unsent_update_count_
Total number of unsent lease updates.
bool isCommunicationInterrupted() const
Checks if communication with the partner is interrupted.
void setPartnerScopes(data::ConstElementPtr new_scopes)
Sets partner scopes.
int getPartnerState() const
Returns last known state of the partner.
bool clockSkewShouldWarn()
Issues a warning about high clock skew between the active servers if one is warranted.
std::string logFormatClockSkew() const
Returns current clock skew value in the logger friendly format.
void setPartnerUnsentUpdateCount(uint64_t unsent_update_count)
Saves new total number of unsent lease updates from the partner.
void setPartnerState(const std::string &state)
Sets partner state.
bool clockSkewShouldTerminate()
Indicates whether the HA service should enter "terminated" state as a result of the clock skew exceed...
std::pair< uint64_t, uint64_t > partner_unsent_update_count_
Previous and current total number of unsent lease updates from the partner.
std::set< std::string > getPartnerScopes() const
Returns scopes served by the partner server.
virtual ~CommunicationState()
Destructor.
HAConfigPtr config_
High availability configuration.
bool isHeartbeatRunning() const
Checks if recurring heartbeat is running.
static size_t getRejectedLeaseUpdatesCountFromContainer(RejectedClientsType &rejected_clients)
Extracts the number of lease updates rejected by the partner from the specified container.
long interval_
Interval specified for the heartbeat.
void setPartnerUnavailable()
Sets partner state unavailable.
void stopHeartbeat()
Stops recurring heartbeat.
void increaseUnsentUpdateCount()
Increases a total number of unsent lease updates by 1.
void setPartnerTime(const std::string &time_text)
Provide partner's notion of time so the new clock skew can be calculated.
bool hasPartnerNewUnsentUpdates() const
Checks if the partner allocated new leases for which it hasn't sent any lease updates.
virtual bool reportSuccessfulLeaseUpdateInternal(const dhcp::PktPtr &message)=0
Marks the lease update successful.
asiolink::IOServicePtr io_service_
Pointer to the common IO service instance.
virtual size_t getRejectedLeaseUpdatesCountInternal()=0
Returns the number of lease updates rejected by the partner.
void modifyPokeTime(const long secs)
Modifies poke time by adding seconds to it.
const boost::scoped_ptr< std::mutex > mutex_
The mutex used to protect internal state.
data::ElementPtr getReport() const
Returns the report about current communication state.
boost::posix_time::ptime getPartnerTimeAtSkew() const
Retrieves the time of the partner node when skew was last calculated.
boost::posix_time::time_duration clock_skew_
Clock skew between the active servers.
size_t getAnalyzedMessagesCount() const
Returns the number of analyzed messages while being in the communications interrupted state.
size_t analyzed_messages_count_
Total number of analyzed messages to be responded by partner.
std::function< void()> heartbeat_impl_
Pointer to the function providing heartbeat implementation.
boost::posix_time::ptime poke_time_
Last poke time.
boost::posix_time::time_duration updatePokeTime()
Update the poke time and compute the duration.
bool reportSuccessfulLeaseUpdate(const dhcp::PktPtr &message)
Marks the lease update successful (MT safe).
boost::posix_time::ptime partner_time_at_skew_
Partner reported time when skew was calculated.
CommunicationState(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
boost::posix_time::time_duration getDurationSincePartnerStateTime() const
Returns the duration since the partner was first seen in the current state.
int partner_state_
Last known state of the partner server.
boost::posix_time::ptime last_clock_skew_warn_
Holds a time when last warning about too high clock skew was issued.
std::set< std::string > partner_scopes_
Last known set of scopes served by the partner server.
static std::vector< uint8_t > getClientId(const dhcp::PktPtr &message, const uint16_t option_type)
Convenience function attempting to retrieve client identifier from the DHCP message.
uint64_t getUnsentUpdateCount() const
Returns a total number of unsent lease updates.
bool rejectedLeaseUpdatesShouldTerminate()
Indicates whether the HA service should enter "terminated" state due to excessive number of rejected ...
boost::posix_time::ptime getMyTimeAtSkew() const
Retrieves the time of the local node when skew was last calculated.
boost::posix_time::ptime my_time_at_skew_
My time when skew was calculated.
int64_t getDurationInMillisecs() const
Returns duration between the poke time and current time.
bool reportRejectedLeaseUpdate(const dhcp::PktPtr &message, const uint32_t lifetime=86400)
Marks that the lease update failed due to a conflict for the specified DHCP message (MT safe).
size_t getRejectedLeaseUpdatesCount()
Returns the number of lease updates rejected by the partner (MT safe).
asiolink::IntervalTimerPtr timer_
Interval timer triggering heartbeat commands.
void poke()
Pokes the communication state.
This class parses and generates time values used in HTTP.
boost::posix_time::ptime getPtime() const
Returns time encapsulated by this class.
static HttpDateTime fromRfc1123(const std::string &time_string)
Creates an instance from a string containing time value formatted as specified in RFC 1123.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
OptionInt< uint16_t > OptionUint16
boost::shared_ptr< OptionUint16 > OptionUint16Ptr
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
boost::shared_ptr< IOService > IOServicePtr
Defines a smart pointer to an IOService instance.
boost::shared_ptr< const Element > ConstElementPtr
boost::shared_ptr< Element > ElementPtr
boost::shared_ptr< isc::dhcp::Pkt > PktPtr
A pointer to either Pkt4 or Pkt6 packet.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
boost::shared_ptr< Option > OptionPtr
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4_UNACKED
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6
isc::log::Logger ha_logger("ha-hooks")
const isc::log::MessageID HA_HIGH_CLOCK_SKEW_CAUSED_TERMINATION
const isc::log::MessageID HA_LEASE_UPDATE_REJECTS_CAUSED_TERMINATION
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6_UNACKED
std::string stateToString(int state)
Returns state name.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4
int stringToState(const std::string &state_name)
Returns state for a given name.
const isc::log::MessageID HA_HIGH_CLOCK_SKEW
std::string ptimeToText(boost::posix_time::ptime t, size_t fsecs_precision=MAX_FSECS_PRECISION)
Converts ptime structure to text.
Defines the logger used by the top-level component of kea-lfc.
Structure holding information about the client which has sent the packet being analyzed.
std::vector< uint8_t > hwaddr_
Structure holding information about the client who has a rejected lease update.
std::vector< uint8_t > hwaddr_
Structure holding information about a client which sent a packet being analyzed.
Structure holding information about the client who has a rejected lease update.