23#include <boost/pointer_cast.hpp>
37using namespace boost::posix_time;
43constexpr long WARN_CLOCK_SKEW = 30;
46constexpr long TERM_CLOCK_SKEW = 60;
49constexpr long MIN_TIME_SINCE_CLOCK_SKEW_WARN = 60;
58 : io_service_(io_service), config_(config), timer_(), interval_(0),
59 poke_time_(
boost::posix_time::microsec_clock::universal_time()),
60 heartbeat_impl_(0), partner_state_(-1), partner_scopes_(),
61 clock_skew_(0, 0, 0, 0), last_clock_skew_warn_(),
62 my_time_at_skew_(), partner_time_at_skew_(),
63 analyzed_messages_count_(0), unsent_update_count_(0),
64 partner_unsent_update_count_{0, 0}, mutex_(new mutex()) {
73 if (MultiThreadingMgr::instance().getMode()) {
74 std::lock_guard<std::mutex> lk(*
mutex_);
75 poke_time_ += boost::posix_time::seconds(secs);
77 poke_time_ += boost::posix_time::seconds(secs);
83 if (MultiThreadingMgr::instance().getMode()) {
84 std::lock_guard<std::mutex> lk(*
mutex_);
93 if (MultiThreadingMgr::instance().getMode()) {
94 std::lock_guard<std::mutex> lk(*
mutex_);
95 setPartnerStateInternal(state);
97 setPartnerStateInternal(state);
102CommunicationState::setPartnerStateInternal(
const std::string& state) {
113 if (MultiThreadingMgr::instance().getMode()) {
114 std::lock_guard<std::mutex> lk(*
mutex_);
123 if (MultiThreadingMgr::instance().getMode()) {
124 std::lock_guard<std::mutex> lk(*
mutex_);
125 setPartnerScopesInternal(new_scopes);
127 setPartnerScopesInternal(new_scopes);
132CommunicationState::setPartnerScopesInternal(
ConstElementPtr new_scopes) {
133 if (!new_scopes || (new_scopes->getType() != Element::list)) {
135 " the received value is not a valid JSON list");
138 std::set<std::string> partner_scopes;
139 for (
auto i = 0; i < new_scopes->size(); ++i) {
140 auto scope = new_scopes->get(i);
141 if (scope->getType() != Element::string) {
143 " the received scope value is not a valid JSON string");
145 auto scope_str = scope->stringValue();
146 if (!scope_str.empty()) {
147 partner_scopes.insert(scope_str);
155 const std::function<
void()>& heartbeat_impl) {
156 if (MultiThreadingMgr::instance().getMode()) {
157 std::lock_guard<std::mutex> lk(*
mutex_);
158 startHeartbeatInternal(interval, heartbeat_impl);
160 startHeartbeatInternal(interval, heartbeat_impl);
165CommunicationState::startHeartbeatInternal(
const long interval,
166 const std::function<
void()>& heartbeat_impl) {
167 bool settings_modified =
false;
171 if (heartbeat_impl) {
172 settings_modified =
true;
179 " to the heartbeat implementation is not specified");
185 settings_modified |= (
interval_ != interval);
192 isc_throw(BadValue,
"unable to start heartbeat when interval"
193 " for the heartbeat timer is not specified");
200 if (settings_modified) {
207 if (MultiThreadingMgr::instance().getMode()) {
208 std::lock_guard<std::mutex> lk(*
mutex_);
209 stopHeartbeatInternal();
211 stopHeartbeatInternal();
216CommunicationState::stopHeartbeatInternal() {
227 if (MultiThreadingMgr::instance().getMode()) {
228 std::lock_guard<std::mutex> lk(*
mutex_);
229 return (
static_cast<bool>(
timer_));
231 return (
static_cast<bool>(
timer_));
235boost::posix_time::time_duration
237 if (MultiThreadingMgr::instance().getMode()) {
238 std::lock_guard<std::mutex> lk(*
mutex_);
239 return (updatePokeTimeInternal());
241 return (updatePokeTimeInternal());
245boost::posix_time::time_duration
246CommunicationState::updatePokeTimeInternal() {
248 boost::posix_time::ptime prev_poke_time =
poke_time_;
250 poke_time_ = boost::posix_time::microsec_clock::universal_time();
256 if (MultiThreadingMgr::instance().getMode()) {
257 std::lock_guard<std::mutex> lk(*
mutex_);
265CommunicationState::pokeInternal() {
267 boost::posix_time::time_duration duration_since_poke = updatePokeTimeInternal();
280 if (duration_since_poke.total_seconds() > 0) {
284 startHeartbeatInternal();
291 if (MultiThreadingMgr::instance().getMode()) {
292 std::lock_guard<std::mutex> lk(*
mutex_);
293 return (getDurationInMillisecsInternal());
295 return (getDurationInMillisecsInternal());
300CommunicationState::getDurationInMillisecsInternal()
const {
301 ptime now = boost::posix_time::microsec_clock::universal_time();
303 return (duration.total_milliseconds());
318 if (MultiThreadingMgr::instance().getMode()) {
319 std::lock_guard<std::mutex> lk(*
mutex_);
320 return (clockSkewShouldWarnInternal());
322 return (clockSkewShouldWarnInternal());
327CommunicationState::clockSkewShouldWarnInternal() {
329 if (isClockSkewGreater(WARN_CLOCK_SKEW)) {
336 ptime now = boost::posix_time::microsec_clock::universal_time();
343 (since_warn_duration.total_seconds() > MIN_TIME_SINCE_CLOCK_SKEW_WARN)) {
346 .arg(logFormatClockSkewInternal());
357 if (MultiThreadingMgr::instance().getMode()) {
358 std::lock_guard<std::mutex> lk(*
mutex_);
360 return (clockSkewShouldTerminateInternal());
362 return (clockSkewShouldTerminateInternal());
367CommunicationState::clockSkewShouldTerminateInternal()
const {
368 if (isClockSkewGreater(TERM_CLOCK_SKEW)) {
370 .arg(logFormatClockSkewInternal());
378CommunicationState::isClockSkewGreater(
const long seconds)
const {
385 if (MultiThreadingMgr::instance().getMode()) {
386 std::lock_guard<std::mutex> lk(*
mutex_);
387 setPartnerTimeInternal(time_text);
389 setPartnerTimeInternal(time_text);
394CommunicationState::setPartnerTimeInternal(
const std::string& time_text) {
402 if (MultiThreadingMgr::instance().getMode()) {
403 std::lock_guard<std::mutex> lk(*
mutex_);
404 return (logFormatClockSkewInternal());
406 return (logFormatClockSkewInternal());
411CommunicationState::logFormatClockSkewInternal()
const {
412 std::ostringstream os;
418 return (
"skew not initialized");
425 <<
", partner's clock is ";
429 os <<
clock_skew_.invert_sign().total_seconds() <<
"s behind";
440 auto report = Element::createMap();
443 report->set(
"in-touch", Element::create(in_touch));
446 report->set(
"age", Element::create(age));
452 report->set(
"last-state", Element::create(std::string()));
455 auto list = Element::createList();
457 list->add(Element::create(scope));
459 report->set(
"last-scopes", list);
460 report->set(
"communication-interrupted",
465 long long unacked_clients_left = 0;
467 unacked_clients_left =
static_cast<long long>(
config_->getMaxUnackedClients() -
470 report->set(
"unacked-clients-left", Element::create(unacked_clients_left));
478 if (MultiThreadingMgr::instance().getMode()) {
479 std::lock_guard<std::mutex> lk(*
mutex_);
488 if (MultiThreadingMgr::instance().getMode()) {
489 std::lock_guard<std::mutex> lk(*
mutex_);
490 increaseUnsentUpdateCountInternal();
492 increaseUnsentUpdateCountInternal();
497CommunicationState::increaseUnsentUpdateCountInternal() {
509 if (MultiThreadingMgr::instance().getMode()) {
510 std::lock_guard<std::mutex> lk(*
mutex_);
511 return (hasPartnerNewUnsentUpdatesInternal());
513 return (hasPartnerNewUnsentUpdatesInternal());
518CommunicationState::hasPartnerNewUnsentUpdatesInternal()
const {
525 if (MultiThreadingMgr::instance().getMode()) {
526 std::lock_guard<std::mutex> lk(*
mutex_);
527 setPartnerUnsentUpdateCountInternal(unsent_update_count);
529 setPartnerUnsentUpdateCountInternal(unsent_update_count);
534CommunicationState::setPartnerUnsentUpdateCountInternal(uint64_t unsent_update_count) {
546 if (MultiThreadingMgr::instance().getMode()) {
547 std::lock_guard<std::mutex> lk(*
mutex_);
557 Pkt4Ptr msg = boost::dynamic_pointer_cast<Pkt4>(message);
566 uint16_t secs = msg->getSecs();
571 if ((secs > 255) && ((secs & 0xFF) == 0)) {
572 secs = ((secs >> 8) | (secs << 8));
579 auto unacked = (secs * 1000 >
config_->getMaxAckDelay());
583 std::vector<uint8_t> client_id;
586 client_id = opt_client_id->getData();
589 bool log_unacked =
false;
593 auto existing_request = idx.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id));
594 if (existing_request != idx.end()) {
599 if (!existing_request->unacked_ && unacked) {
601 idx.replace(existing_request, connecting_client);
609 idx.insert(connecting_client);
610 log_unacked = unacked;
617 .arg(message->getLabel());
623 unsigned unacked_left = 0;
625 if (
config_->getMaxUnackedClients() >= unacked_total) {
626 unacked_left =
config_->getMaxUnackedClients() - unacked_total + 1;
629 .arg(message->getLabel())
637 if (MultiThreadingMgr::instance().getMode()) {
638 std::lock_guard<std::mutex> lk(*
mutex_);
647 return ((
config_->getMaxUnackedClients() == 0) ||
649 config_->getMaxUnackedClients()));
654 if (MultiThreadingMgr::instance().getMode()) {
655 std::lock_guard<std::mutex> lk(*
mutex_);
664 if (MultiThreadingMgr::instance().getMode()) {
665 std::lock_guard<std::mutex> lk(*
mutex_);
684 if (MultiThreadingMgr::instance().getMode()) {
685 std::lock_guard<std::mutex> lk(*
mutex_);
695 Pkt6Ptr msg = boost::dynamic_pointer_cast<Pkt6>(message);
707 auto unacked = (elapsed_time && elapsed_time->getValue() * 10 >
config_->getMaxAckDelay());
715 bool log_unacked =
false;
719 auto existing_request = idx.find(duid->getData());
720 if (existing_request != idx.end()) {
725 if (!existing_request->unacked_ && unacked) {
727 idx.replace(existing_request, connecting_client);
735 idx.insert(connecting_client);
736 log_unacked = unacked;
743 .arg(message->getLabel());
749 unsigned unacked_left = 0;
751 if (
config_->getMaxUnackedClients() >= unacked_total) {
752 unacked_left =
config_->getMaxUnackedClients() - unacked_total + 1;
755 .arg(message->getLabel())
763 if (MultiThreadingMgr::instance().getMode()) {
764 std::lock_guard<std::mutex> lk(*
mutex_);
773 return ((
config_->getMaxUnackedClients() == 0) ||
775 config_->getMaxUnackedClients()));
780 if (MultiThreadingMgr::instance().getMode()) {
781 std::lock_guard<std::mutex> lk(*
mutex_);
790 if (MultiThreadingMgr::instance().getMode()) {
791 std::lock_guard<std::mutex> lk(*
mutex_);
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.
Forward declaration to OptionInt.
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
virtual size_t getUnackedClientsCount() const
Returns the current number of clients which haven't gotten a lease from the partner server.
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.
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.
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 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 size_t getConnectingClientsCount() const
Returns the current number of clients which attempted to get a lease from the partner server.
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 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 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.
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.
long interval_
Interval specified for the heartbeat.
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.
asiolink::IOServicePtr io_service_
Pointer to the common IO service instance.
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::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.
bool clockSkewShouldTerminate() const
Indicates whether the HA service should enter "terminated" state as a result of the clock skew exceed...
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.
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.
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.
uint64_t getUnsentUpdateCount() const
Returns a total number of unsent lease updates.
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.
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.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
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
@ DHO_DHCP_CLIENT_IDENTIFIER
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_HIGH_CLOCK_SKEW_CAUSES_TERMINATION
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4_UNACKED
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6
isc::log::Logger ha_logger("ha-hooks")
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 send the packet being analyzed.
std::vector< uint8_t > hwaddr_
Structure holding information about a client which sent a packet being analyzed.