23#include <boost/pointer_cast.hpp>
36using namespace boost::posix_time;
42constexpr long WARN_CLOCK_SKEW = 30;
45constexpr long TERM_CLOCK_SKEW = 60;
48constexpr long MIN_TIME_SINCE_CLOCK_SKEW_WARN = 60;
57 : io_service_(io_service), config_(config), timer_(), interval_(0),
58 poke_time_(
boost::posix_time::microsec_clock::universal_time()),
59 heartbeat_impl_(0), partner_state_(-1), partner_scopes_(),
60 clock_skew_(0, 0, 0, 0), last_clock_skew_warn_(),
61 my_time_at_skew_(), partner_time_at_skew_(),
62 analyzed_messages_count_(0), mutex_(new mutex()) {
71 if (MultiThreadingMgr::instance().getMode()) {
72 std::lock_guard<std::mutex> lk(*
mutex_);
73 poke_time_ += boost::posix_time::seconds(secs);
75 poke_time_ += boost::posix_time::seconds(secs);
81 if (MultiThreadingMgr::instance().getMode()) {
82 std::lock_guard<std::mutex> lk(*
mutex_);
91 if (MultiThreadingMgr::instance().getMode()) {
92 std::lock_guard<std::mutex> lk(*
mutex_);
93 setPartnerStateInternal(state);
95 setPartnerStateInternal(state);
100CommunicationState::setPartnerStateInternal(
const std::string& state) {
111 if (MultiThreadingMgr::instance().getMode()) {
112 std::lock_guard<std::mutex> lk(*
mutex_);
121 if (MultiThreadingMgr::instance().getMode()) {
122 std::lock_guard<std::mutex> lk(*
mutex_);
123 setPartnerScopesInternal(new_scopes);
125 setPartnerScopesInternal(new_scopes);
130CommunicationState::setPartnerScopesInternal(
ConstElementPtr new_scopes) {
131 if (!new_scopes || (new_scopes->getType() != Element::list)) {
133 " the received value is not a valid JSON list");
136 std::set<std::string> partner_scopes;
137 for (
auto i = 0; i < new_scopes->size(); ++i) {
138 auto scope = new_scopes->get(i);
139 if (scope->getType() != Element::string) {
141 " the received scope value is not a valid JSON string");
143 auto scope_str = scope->stringValue();
144 if (!scope_str.empty()) {
145 partner_scopes.insert(scope_str);
153 const std::function<
void()>& heartbeat_impl) {
154 if (MultiThreadingMgr::instance().getMode()) {
155 std::lock_guard<std::mutex> lk(*
mutex_);
156 startHeartbeatInternal(interval, heartbeat_impl);
158 startHeartbeatInternal(interval, heartbeat_impl);
163CommunicationState::startHeartbeatInternal(
const long interval,
164 const std::function<
void()>& heartbeat_impl) {
165 bool settings_modified =
false;
169 if (heartbeat_impl) {
170 settings_modified =
true;
177 " to the heartbeat implementation is not specified");
183 settings_modified |= (
interval_ != interval);
190 isc_throw(BadValue,
"unable to start heartbeat when interval"
191 " for the heartbeat timer is not specified");
198 if (settings_modified) {
205 if (MultiThreadingMgr::instance().getMode()) {
206 std::lock_guard<std::mutex> lk(*
mutex_);
207 stopHeartbeatInternal();
209 stopHeartbeatInternal();
214CommunicationState::stopHeartbeatInternal() {
225 if (MultiThreadingMgr::instance().getMode()) {
226 std::lock_guard<std::mutex> lk(*
mutex_);
227 return (
static_cast<bool>(
timer_));
229 return (
static_cast<bool>(
timer_));
233boost::posix_time::time_duration
235 if (MultiThreadingMgr::instance().getMode()) {
236 std::lock_guard<std::mutex> lk(*
mutex_);
237 return (updatePokeTimeInternal());
239 return (updatePokeTimeInternal());
243boost::posix_time::time_duration
244CommunicationState::updatePokeTimeInternal() {
246 boost::posix_time::ptime prev_poke_time =
poke_time_;
248 poke_time_ = boost::posix_time::microsec_clock::universal_time();
254 if (MultiThreadingMgr::instance().getMode()) {
255 std::lock_guard<std::mutex> lk(*
mutex_);
263CommunicationState::pokeInternal() {
265 boost::posix_time::time_duration duration_since_poke = updatePokeTimeInternal();
279 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));
483 if (MultiThreadingMgr::instance().getMode()) {
484 std::lock_guard<std::mutex> lk(*
mutex_);
494 Pkt4Ptr msg = boost::dynamic_pointer_cast<Pkt4>(message);
503 uint16_t secs = msg->getSecs();
508 if ((secs > 255) && ((secs & 0xFF) == 0)) {
509 secs = ((secs >> 8) | (secs << 8));
516 auto unacked = (secs * 1000 >
config_->getMaxAckDelay());
520 std::vector<uint8_t> client_id;
523 client_id = opt_client_id->getData();
526 bool log_unacked =
false;
530 auto existing_request = idx.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id));
531 if (existing_request != idx.end()) {
536 if (!existing_request->unacked_ && unacked) {
538 idx.replace(existing_request, connecting_client);
546 idx.insert(connecting_client);
547 log_unacked = unacked;
554 .arg(message->getLabel());
560 unsigned unacked_left = 0;
562 if (
config_->getMaxUnackedClients() >= unacked_total) {
563 unacked_left =
config_->getMaxUnackedClients() - unacked_total + 1;
566 .arg(message->getLabel())
574 if (MultiThreadingMgr::instance().getMode()) {
575 std::lock_guard<std::mutex> lk(*
mutex_);
584 return ((
config_->getMaxUnackedClients() == 0) ||
586 config_->getMaxUnackedClients()));
591 if (MultiThreadingMgr::instance().getMode()) {
592 std::lock_guard<std::mutex> lk(*
mutex_);
601 if (MultiThreadingMgr::instance().getMode()) {
602 std::lock_guard<std::mutex> lk(*
mutex_);
621 if (MultiThreadingMgr::instance().getMode()) {
622 std::lock_guard<std::mutex> lk(*
mutex_);
632 Pkt6Ptr msg = boost::dynamic_pointer_cast<Pkt6>(message);
644 auto unacked = (elapsed_time && elapsed_time->getValue() * 10 >
config_->getMaxAckDelay());
652 bool log_unacked =
false;
656 auto existing_request = idx.find(duid->getData());
657 if (existing_request != idx.end()) {
662 if (!existing_request->unacked_ && unacked) {
664 idx.replace(existing_request, connecting_client);
672 idx.insert(connecting_client);
673 log_unacked = unacked;
680 .arg(message->getLabel());
686 unsigned unacked_left = 0;
688 if (
config_->getMaxUnackedClients() >= unacked_total) {
689 unacked_left =
config_->getMaxUnackedClients() - unacked_total + 1;
692 .arg(message->getLabel())
700 if (MultiThreadingMgr::instance().getMode()) {
701 std::lock_guard<std::mutex> lk(*
mutex_);
710 return ((
config_->getMaxUnackedClients() == 0) ||
712 config_->getMaxUnackedClients()));
717 if (MultiThreadingMgr::instance().getMode()) {
718 std::lock_guard<std::mutex> lk(*
mutex_);
727 if (MultiThreadingMgr::instance().getMode()) {
728 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).
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 setPartnerState(const std::string &state)
Sets partner state.
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 setPartnerTime(const std::string &time_text)
Provide partner's notion of time so the new clock skew can be calculated.
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.
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.