23 #include <boost/pointer_cast.hpp>
42 constexpr
long WARN_CLOCK_SKEW = 30;
45 constexpr
long TERM_CLOCK_SKEW = 60;
48 constexpr
long MIN_TIME_SINCE_CLOCK_SKEW_WARN = 60;
55 CommunicationState::CommunicationState(
const IOServicePtr& io_service,
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);
100 CommunicationState::setPartnerStateInternal(
const std::string& state) {
109 std::set<std::string>
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);
130 CommunicationState::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);
163 CommunicationState::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();
214 CommunicationState::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_));
233 boost::posix_time::time_duration
235 if (MultiThreadingMgr::instance().getMode()) {
236 std::lock_guard<std::mutex> lk(*
mutex_);
237 return (updatePokeTimeInternal());
239 return (updatePokeTimeInternal());
243 boost::posix_time::time_duration
244 CommunicationState::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_);
263 CommunicationState::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());
300 CommunicationState::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());
327 CommunicationState::clockSkewShouldWarnInternal() {
329 if (isClockSkewGreater(WARN_CLOCK_SKEW)) {
336 ptime now = boost::posix_time::microsec_clock::universal_time();
342 if (last_clock_skew_warn_.is_not_a_date_time() ||
343 (since_warn_duration.total_seconds() > MIN_TIME_SINCE_CLOCK_SKEW_WARN)) {
344 last_clock_skew_warn_ = now;
346 .arg(logFormatClockSkewInternal());
357 if (MultiThreadingMgr::instance().getMode()) {
358 std::lock_guard<std::mutex> lk(*
mutex_);
360 return (clockSkewShouldTerminateInternal());
362 return (clockSkewShouldTerminateInternal());
367 CommunicationState::clockSkewShouldTerminateInternal()
const {
368 if (isClockSkewGreater(TERM_CLOCK_SKEW)) {
370 .arg(logFormatClockSkewInternal());
378 CommunicationState::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);
394 CommunicationState::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());
411 CommunicationState::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_);
int stringToState(const std::string &state_name)
Returns state for a given name.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
CommunicationState6(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
virtual size_t getConnectingClientsCount() const =0
Returns the current number of clients which attempted to get a lease from the partner server...
virtual bool failureDetected() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
virtual void clearConnectingClients()=0
Removes information about the clients the partner server should respond to while communication with t...
bool isCommunicationInterrupted() const
Checks if communication with the partner is interrupted.
void setPartnerTime(const std::string &time_text)
Provide partner's notion of time so the new clock skew can be calculated.
Holds communication state between the two HA peers.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
ConnectingClients4 connecting_clients_
Holds information about the clients attempting to contact the partner server while the servers are in...
virtual bool failureDetectedInternal() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
boost::shared_ptr< Option > OptionPtr
virtual size_t getUnackedClientsCount() const
Returns the current number of clients which haven't gotten a lease from the partner server...
Represents a DHCPv6 packet.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
boost::shared_ptr< IOService > IOServicePtr
Defines a smart pointer to an IOService instance.
boost::shared_ptr< Element > ElementPtr
std::function< void()> heartbeat_impl_
Pointer to the function providing heartbeat implementation.
asiolink::IOServicePtr io_service_
Pointer to the common IO service instance.
Forward declaration to OptionInt.
Structure holding information about a client which sent a packet being analyzed.
virtual void clearConnectingClients()
Removes information about the clients the partner server should respond to while communication with t...
const isc::log::MessageID HA_HIGH_CLOCK_SKEW
virtual void analyzeMessageInternal(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv6 message appears to be unanswered.
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.
long interval_
Interval specified for the heartbeat.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6_UNACKED
boost::posix_time::ptime poke_time_
Last poke time.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
boost::shared_ptr< OptionUint16 > OptionUint16Ptr
virtual bool failureDetected() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
The IntervalTimer class is a wrapper for the ASIO boost::asio::deadline_timer class.
int getPartnerState() const
Returns last known state of the partner.
void modifyPokeTime(const long secs)
Modifies poke time by adding seconds to it.
int partner_state_
Last known state of the partner server.
std::string ptimeToText(boost::posix_time::ptime t, size_t fsecs_precision=MAX_FSECS_PRECISION)
Converts ptime structure to text.
const boost::scoped_ptr< std::mutex > mutex_
The mutex used to protect internal state.
size_t getAnalyzedMessagesCount() const
Returns the number of analyzed messages while being in the communications interrupted state...
std::string stateToString(int state)
Returns state name.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
boost::shared_ptr< const Element > ConstElementPtr
virtual void analyzeMessage(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv6 message appears to be unanswered.
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...
std::set< std::string > getPartnerScopes() const
Returns scopes served by the partner server.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4_UNACKED
void stopHeartbeat()
Stops recurring heartbeat.
This class parses and generates time values used in HTTP.
virtual size_t getUnackedClientsCount() const
Returns the current number of clients which haven't gotten a lease from the partner server...
Defines the logger used by the top-level component of kea-dhcp-ddns.
bool clockSkewShouldTerminate() const
Indicates whether the HA service should enter "terminated" state as a result of the clock skew exceed...
void poke()
Pokes the communication state.
Represents DHCPv4 packet.
boost::posix_time::time_duration clock_skew_
Clock skew between the active servers.
const isc::log::MessageID HA_HIGH_CLOCK_SKEW_CAUSES_TERMINATION
CommunicationState4(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4
virtual void analyzeMessage(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv4 message appears to be unanswered.
bool isHeartbeatRunning() const
Checks if recurring heartbeat is running.
virtual bool failureDetectedInternal() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
ConnectingClients6 connecting_clients_
Holds information about the clients attempting to contact the partner server while the servers are in...
std::vector< uint8_t > hwaddr_
data::ElementPtr getReport() const
Returns the report about current communication state.
boost::posix_time::ptime my_time_at_skew_
My time when skew was calculated.
void setPartnerState(const std::string &state)
Sets partner state.
void startHeartbeat(const long interval, const std::function< void()> &heartbeat_impl)
Starts recurring heartbeat (public interface).
isc::log::Logger ha_logger("ha-hooks")
std::set< std::string > partner_scopes_
Last known set of scopes served by the partner server.
virtual ~CommunicationState()
Destructor.
Structure holding information about the client which has send the packet being analyzed.
bool clockSkewShouldWarn()
Issues a warning about high clock skew between the active servers if one is warranted.
boost::posix_time::ptime last_clock_skew_warn_
Holds a time when last warning about too high clock skew was issued.
std::string logFormatClockSkew() const
Returns current clock skew value in the logger friendly format.
asiolink::IntervalTimerPtr timer_
Interval timer triggering heartbeat commands.
void setPartnerScopes(data::ConstElementPtr new_scopes)
Sets partner scopes.
int64_t getDurationInMillisecs() const
Returns duration between the poke time and current time.
virtual size_t getUnackedClientsCount() const =0
Returns the current number of clients which haven't got the lease from the partner server...
HAConfigPtr config_
High availability configuration.
virtual void clearConnectingClients()
Removes information about the clients the partner server should respond to while communication with t...
virtual size_t getConnectingClientsCount() const
Returns the current number of clients which attempted to get 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.
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
virtual size_t getConnectingClientsCount() const
Returns the current number of clients which attempted to get a lease from the partner server...
size_t analyzed_messages_count_
Total number of analyzed messages to be responded by partner.