Kea  1.9.9-git
communication_state.cc
Go to the documentation of this file.
1 // Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <communication_state.h>
10 #include <ha_log.h>
11 #include <ha_service_states.h>
12 #include <cc/data.h>
13 #include <exceptions/exceptions.h>
14 #include <dhcp/dhcp4.h>
15 #include <dhcp/dhcp6.h>
16 #include <dhcp/option_int.h>
17 #include <dhcp/pkt4.h>
18 #include <dhcp/pkt6.h>
19 #include <http/date_time.h>
20 #include <util/boost_time_utils.h>
22 
23 #include <boost/pointer_cast.hpp>
24 
25 #include <functional>
26 #include <sstream>
27 #include <utility>
28 
29 using namespace isc::asiolink;
30 using namespace isc::data;
31 using namespace isc::dhcp;
32 using namespace isc::http;
33 using namespace isc::log;
34 using namespace isc::util;
35 
36 using namespace boost::posix_time;
37 using namespace std;
38 
39 namespace {
40 
42 constexpr long WARN_CLOCK_SKEW = 30;
43 
45 constexpr long TERM_CLOCK_SKEW = 60;
46 
48 constexpr long MIN_TIME_SINCE_CLOCK_SKEW_WARN = 60;
49 
50 }
51 
52 namespace isc {
53 namespace ha {
54 
55 CommunicationState::CommunicationState(const IOServicePtr& io_service,
56  const HAConfigPtr& config)
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()) {
63 }
64 
66  stopHeartbeat();
67 }
68 
69 void
71  if (MultiThreadingMgr::instance().getMode()) {
72  std::lock_guard<std::mutex> lk(*mutex_);
73  poke_time_ += boost::posix_time::seconds(secs);
74  } else {
75  poke_time_ += boost::posix_time::seconds(secs);
76  }
77 }
78 
79 int
81  if (MultiThreadingMgr::instance().getMode()) {
82  std::lock_guard<std::mutex> lk(*mutex_);
83  return (partner_state_);
84  } else {
85  return (partner_state_);
86  }
87 }
88 
89 void
90 CommunicationState::setPartnerState(const std::string& state) {
91  if (MultiThreadingMgr::instance().getMode()) {
92  std::lock_guard<std::mutex> lk(*mutex_);
93  setPartnerStateInternal(state);
94  } else {
95  setPartnerStateInternal(state);
96  }
97 }
98 
99 void
100 CommunicationState::setPartnerStateInternal(const std::string& state) {
101  try {
102  partner_state_ = stringToState(state);
103  } catch (...) {
104  isc_throw(BadValue, "unsupported HA partner state returned "
105  << state);
106  }
107 }
108 
109 std::set<std::string>
111  if (MultiThreadingMgr::instance().getMode()) {
112  std::lock_guard<std::mutex> lk(*mutex_);
113  return (partner_scopes_);
114  } else {
115  return (partner_scopes_);
116  }
117 }
118 
119 void
121  if (MultiThreadingMgr::instance().getMode()) {
122  std::lock_guard<std::mutex> lk(*mutex_);
123  setPartnerScopesInternal(new_scopes);
124  } else {
125  setPartnerScopesInternal(new_scopes);
126  }
127 }
128 
129 void
130 CommunicationState::setPartnerScopesInternal(ConstElementPtr new_scopes) {
131  if (!new_scopes || (new_scopes->getType() != Element::list)) {
132  isc_throw(BadValue, "unable to record partner's HA scopes because"
133  " the received value is not a valid JSON list");
134  }
135 
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) {
140  isc_throw(BadValue, "unable to record partner's HA scopes because"
141  " the received scope value is not a valid JSON string");
142  }
143  auto scope_str = scope->stringValue();
144  if (!scope_str.empty()) {
145  partner_scopes.insert(scope_str);
146  }
147  }
148  partner_scopes_ = partner_scopes;
149 }
150 
151 void
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);
157  } else {
158  startHeartbeatInternal(interval, heartbeat_impl);
159  }
160 }
161 
162 void
163 CommunicationState::startHeartbeatInternal(const long interval,
164  const std::function<void()>& heartbeat_impl) {
165  bool settings_modified = false;
166 
167  // If we're setting the heartbeat for the first time, it should
168  // be non-null.
169  if (heartbeat_impl) {
170  settings_modified = true;
171  heartbeat_impl_ = heartbeat_impl;
172 
173  } else if (!heartbeat_impl_) {
174  // The heartbeat is re-scheduled but we have no historic implementation
175  // pointer we could re-use. This is a programmatic issue.
176  isc_throw(BadValue, "unable to start heartbeat when pointer"
177  " to the heartbeat implementation is not specified");
178  }
179 
180  // If we're setting the heartbeat for the first time, the interval
181  // should be greater than 0.
182  if (interval != 0) {
183  settings_modified |= (interval_ != interval);
184  interval_ = interval;
185 
186  } else if (interval_ <= 0) {
187  // The heartbeat is re-scheduled but we have no historic interval
188  // which we could re-use. This is a programmatic issue.
189  heartbeat_impl_ = 0;
190  isc_throw(BadValue, "unable to start heartbeat when interval"
191  " for the heartbeat timer is not specified");
192  }
193 
194  if (!timer_) {
195  timer_.reset(new IntervalTimer(*io_service_));
196  }
197 
198  if (settings_modified) {
199  timer_->setup(heartbeat_impl_, interval_, IntervalTimer::ONE_SHOT);
200  }
201 }
202 
203 void
205  if (MultiThreadingMgr::instance().getMode()) {
206  std::lock_guard<std::mutex> lk(*mutex_);
207  stopHeartbeatInternal();
208  } else {
209  stopHeartbeatInternal();
210  }
211 }
212 
213 void
214 CommunicationState::stopHeartbeatInternal() {
215  if (timer_) {
216  timer_->cancel();
217  timer_.reset();
218  interval_ = 0;
219  heartbeat_impl_ = 0;
220  }
221 }
222 
223 bool
225  if (MultiThreadingMgr::instance().getMode()) {
226  std::lock_guard<std::mutex> lk(*mutex_);
227  return (static_cast<bool>(timer_));
228  } else {
229  return (static_cast<bool>(timer_));
230  }
231 }
232 
233 boost::posix_time::time_duration
235  if (MultiThreadingMgr::instance().getMode()) {
236  std::lock_guard<std::mutex> lk(*mutex_);
237  return (updatePokeTimeInternal());
238  } else {
239  return (updatePokeTimeInternal());
240  }
241 }
242 
243 boost::posix_time::time_duration
244 CommunicationState::updatePokeTimeInternal() {
245  // Remember previous poke time.
246  boost::posix_time::ptime prev_poke_time = poke_time_;
247  // Set poke time to the current time.
248  poke_time_ = boost::posix_time::microsec_clock::universal_time();
249  return (poke_time_ - prev_poke_time);
250 }
251 
252 void
254  if (MultiThreadingMgr::instance().getMode()) {
255  std::lock_guard<std::mutex> lk(*mutex_);
256  pokeInternal();
257  } else {
258  pokeInternal();
259  }
260 }
261 
262 void
263 CommunicationState::pokeInternal() {
264  // Update poke time and compute duration.
265  boost::posix_time::time_duration duration_since_poke = updatePokeTimeInternal();
266 
267  // If we have been tracking the DHCP messages directed to the partner,
268  // we need to clear any gathered information because the connection
269  // seems to be (re)established.
272 
273  if (timer_) {
274  // Check the duration since last poke. If it is less than a second, we don't
275  // want to reschedule the timer. The only case when the poke time duration is
276  // lower than 1s is when we're performing lease updates. In order to avoid the
277  // overhead of re-scheduling the timer too frequently we reschedule it only if the
278  // duration is 1s or more. This matches the time resolution for heartbeats.
279  if (duration_since_poke.total_seconds() > 0) {
280  // A poke causes the timer to be re-scheduled to prevent it
281  // from triggering a heartbeat shortly after confirming the
282  // connection is ok, based on the lease update or another
283  // command.
284  startHeartbeatInternal();
285  }
286  }
287 }
288 
289 int64_t
291  if (MultiThreadingMgr::instance().getMode()) {
292  std::lock_guard<std::mutex> lk(*mutex_);
293  return (getDurationInMillisecsInternal());
294  } else {
295  return (getDurationInMillisecsInternal());
296  }
297 }
298 
299 int64_t
300 CommunicationState::getDurationInMillisecsInternal() const {
301  ptime now = boost::posix_time::microsec_clock::universal_time();
302  time_duration duration = now - poke_time_;
303  return (duration.total_milliseconds());
304 }
305 
306 bool
308  return (getDurationInMillisecs() > config_->getMaxResponseDelay());
309 }
310 
311 size_t
313  return (analyzed_messages_count_);
314 }
315 
316 bool
318  if (MultiThreadingMgr::instance().getMode()) {
319  std::lock_guard<std::mutex> lk(*mutex_);
320  return (clockSkewShouldWarnInternal());
321  } else {
322  return (clockSkewShouldWarnInternal());
323  }
324 }
325 
326 bool
327 CommunicationState::clockSkewShouldWarnInternal() {
328  // First check if the clock skew is beyond the threshold.
329  if (isClockSkewGreater(WARN_CLOCK_SKEW)) {
330 
331  // In order to prevent to frequent warnings we provide a gating mechanism
332  // which doesn't allow for issuing a warning earlier than 60 seconds after
333  // the previous one.
334 
335  // Find the current time and the duration since last warning.
336  ptime now = boost::posix_time::microsec_clock::universal_time();
337  time_duration since_warn_duration = now - last_clock_skew_warn_;
338 
339  // If the last warning was issued more than 60 seconds ago or it is a
340  // first warning, we need to update the last warning timestamp and return
341  // true to indicate that new warning should be issued.
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());
347  return (true);
348  }
349  }
350 
351  // The warning should not be issued.
352  return (false);
353 }
354 
355 bool
357  if (MultiThreadingMgr::instance().getMode()) {
358  std::lock_guard<std::mutex> lk(*mutex_);
359  // Issue a warning if the clock skew is greater than 60s.
360  return (clockSkewShouldTerminateInternal());
361  } else {
362  return (clockSkewShouldTerminateInternal());
363  }
364 }
365 
366 bool
367 CommunicationState::clockSkewShouldTerminateInternal() const {
368  if (isClockSkewGreater(TERM_CLOCK_SKEW)) {
370  .arg(logFormatClockSkewInternal());
371  return (true);
372  }
373 
374  return (false);
375 }
376 
377 bool
378 CommunicationState::isClockSkewGreater(const long seconds) const {
379  return ((clock_skew_.total_seconds() > seconds) ||
380  (clock_skew_.total_seconds() < -seconds));
381 }
382 
383 void
384 CommunicationState::setPartnerTime(const std::string& time_text) {
385  if (MultiThreadingMgr::instance().getMode()) {
386  std::lock_guard<std::mutex> lk(*mutex_);
387  setPartnerTimeInternal(time_text);
388  } else {
389  setPartnerTimeInternal(time_text);
390  }
391 }
392 
393 void
394 CommunicationState::setPartnerTimeInternal(const std::string& time_text) {
398 }
399 
400 std::string
402  if (MultiThreadingMgr::instance().getMode()) {
403  std::lock_guard<std::mutex> lk(*mutex_);
404  return (logFormatClockSkewInternal());
405  } else {
406  return (logFormatClockSkewInternal());
407  }
408 }
409 
410 std::string
411 CommunicationState::logFormatClockSkewInternal() const {
412  std::ostringstream os;
413 
414  if ((my_time_at_skew_.is_not_a_date_time()) ||
415  (partner_time_at_skew_.is_not_a_date_time())) {
416  // Guard against being called before times have been set.
417  // Otherwise we'll get out-range exceptions.
418  return ("skew not initialized");
419  }
420 
421  // Note HttpTime resolution is only to seconds, so we use fractional
422  // precision of zero when logging.
423  os << "my time: " << util::ptimeToText(my_time_at_skew_, 0)
424  << ", partner's time: " << util::ptimeToText(partner_time_at_skew_, 0)
425  << ", partner's clock is ";
426 
427  // If negative clock skew, the partner's time is behind our time.
428  if (clock_skew_.is_negative()) {
429  os << clock_skew_.invert_sign().total_seconds() << "s behind";
430  } else {
431  // Partner's time is ahead of ours.
432  os << clock_skew_.total_seconds() << "s ahead";
433  }
434 
435  return (os.str());
436 }
437 
440  auto report = Element::createMap();
441 
442  auto in_touch = (getPartnerState() > 0);
443  report->set("in-touch", Element::create(in_touch));
444 
445  auto age = in_touch ? static_cast<long long int>(getDurationInMillisecs() / 1000) : 0;
446  report->set("age", Element::create(age));
447 
448  try {
449  report->set("last-state", Element::create(stateToString(getPartnerState())));
450 
451  } catch (...) {
452  report->set("last-state", Element::create(std::string()));
453  }
454 
455  auto list = Element::createList();
456  for (auto scope : getPartnerScopes()) {
457  list->add(Element::create(scope));
458  }
459  report->set("last-scopes", list);
460  report->set("communication-interrupted",
461  Element::create(isCommunicationInterrupted()));
462  report->set("connecting-clients", Element::create(static_cast<long long>(getConnectingClientsCount())));
463  report->set("unacked-clients", Element::create(static_cast<long long>(getUnackedClientsCount())));
464 
465  long long unacked_clients_left = 0;
466  if (isCommunicationInterrupted() && (config_->getMaxUnackedClients() >= getUnackedClientsCount())) {
467  unacked_clients_left = static_cast<long long>(config_->getMaxUnackedClients() -
468  getUnackedClientsCount() + 1);
469  }
470  report->set("unacked-clients-left", Element::create(unacked_clients_left));
471  report->set("analyzed-packets", Element::create(static_cast<long long>(getAnalyzedMessagesCount())));
472 
473  return (report);
474 }
475 
477  const HAConfigPtr& config)
478  : CommunicationState(io_service, config), connecting_clients_() {
479 }
480 
481 void
482 CommunicationState4::analyzeMessage(const boost::shared_ptr<dhcp::Pkt>& message) {
483  if (MultiThreadingMgr::instance().getMode()) {
484  std::lock_guard<std::mutex> lk(*mutex_);
485  analyzeMessageInternal(message);
486  } else {
487  analyzeMessageInternal(message);
488  }
489 }
490 
491 void
492 CommunicationState4::analyzeMessageInternal(const boost::shared_ptr<dhcp::Pkt>& message) {
493  // The DHCP message must successfully cast to a Pkt4 object.
494  Pkt4Ptr msg = boost::dynamic_pointer_cast<Pkt4>(message);
495  if (!msg) {
496  isc_throw(BadValue, "DHCP message to be analyzed is not a DHCPv4 message");
497  }
498 
500 
501  // Check value of the "secs" field by comparing it with the configured
502  // threshold.
503  uint16_t secs = msg->getSecs();
504 
505  // It was observed that some Windows clients may send swapped bytes in the
506  // "secs" field. When the second byte is 0 and the first byte is non-zero
507  // we consider bytes to be swapped and so we correct them.
508  if ((secs > 255) && ((secs & 0xFF) == 0)) {
509  secs = ((secs >> 8) | (secs << 8));
510  }
511 
512  // Check the value of the "secs" field. The "secs" field holds a value in
513  // seconds, hence we have to multiple by 1000 to get a value in milliseconds.
514  // If the secs value is above the threshold, it means that the current
515  // client should be considered unacked.
516  auto unacked = (secs * 1000 > config_->getMaxAckDelay());
517 
518  // Client identifier will be stored together with the hardware address. It
519  // may remain empty if the client hasn't specified it.
520  std::vector<uint8_t> client_id;
521  OptionPtr opt_client_id = msg->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
522  if (opt_client_id) {
523  client_id = opt_client_id->getData();
524  }
525 
526  bool log_unacked = false;
527 
528  // Check if the given client was already recorded.
529  auto& idx = connecting_clients_.get<0>();
530  auto existing_request = idx.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id));
531  if (existing_request != idx.end()) {
532  // If the client was recorded and was not considered unacked
533  // but it should be considered unacked as a result of processing
534  // this packet, let's update the recorded request to mark the
535  // client unacked.
536  if (!existing_request->unacked_ && unacked) {
537  ConnectingClient4 connecting_client{ msg->getHWAddr()->hwaddr_, client_id, unacked };
538  idx.replace(existing_request, connecting_client);
539  log_unacked = true;
540  }
541 
542  } else {
543  // This is the first time we see the packet from this client. Let's
544  // record it.
545  ConnectingClient4 connecting_client{ msg->getHWAddr()->hwaddr_, client_id, unacked };
546  idx.insert(connecting_client);
547  log_unacked = unacked;
548 
549  if (!unacked) {
550  // This is the first time we see this client after getting into the
551  // communication interrupted state. But, this client hasn't been
552  // yet trying log enough to be considered unacked.
554  .arg(message->getLabel());
555  }
556  }
557 
558  // Only log the first time we detect a client is unacked.
559  if (log_unacked) {
560  unsigned unacked_left = 0;
561  unsigned unacked_total = connecting_clients_.get<1>().count(true);
562  if (config_->getMaxUnackedClients() >= unacked_total) {
563  unacked_left = config_->getMaxUnackedClients() - unacked_total + 1;
564  }
566  .arg(message->getLabel())
567  .arg(unacked_total)
568  .arg(unacked_left);
569  }
570 }
571 
572 bool
574  if (MultiThreadingMgr::instance().getMode()) {
575  std::lock_guard<std::mutex> lk(*mutex_);
576  return (failureDetectedInternal());
577  } else {
578  return (failureDetectedInternal());
579  }
580 }
581 
582 bool
584  return ((config_->getMaxUnackedClients() == 0) ||
585  (connecting_clients_.get<1>().count(true) >
586  config_->getMaxUnackedClients()));
587 }
588 
589 size_t
591  if (MultiThreadingMgr::instance().getMode()) {
592  std::lock_guard<std::mutex> lk(*mutex_);
593  return (connecting_clients_.size());
594  } else {
595  return (connecting_clients_.size());
596  }
597 }
598 
599 size_t
601  if (MultiThreadingMgr::instance().getMode()) {
602  std::lock_guard<std::mutex> lk(*mutex_);
603  return (connecting_clients_.get<1>().count(true));
604  } else {
605  return (connecting_clients_.get<1>().count(true));
606  }
607 }
608 
609 void
611  connecting_clients_.clear();
612 }
613 
615  const HAConfigPtr& config)
616  : CommunicationState(io_service, config), connecting_clients_() {
617 }
618 
619 void
620 CommunicationState6::analyzeMessage(const boost::shared_ptr<dhcp::Pkt>& message) {
621  if (MultiThreadingMgr::instance().getMode()) {
622  std::lock_guard<std::mutex> lk(*mutex_);
623  analyzeMessageInternal(message);
624  } else {
625  analyzeMessageInternal(message);
626  }
627 }
628 
629 void
630 CommunicationState6::analyzeMessageInternal(const boost::shared_ptr<dhcp::Pkt>& message) {
631  // The DHCP message must successfully cast to a Pkt6 object.
632  Pkt6Ptr msg = boost::dynamic_pointer_cast<Pkt6>(message);
633  if (!msg) {
634  isc_throw(BadValue, "DHCP message to be analyzed is not a DHCPv6 message");
635  }
636 
638 
639  // Check the value of the "elapsed time" option. If it is below the threshold
640  // there is nothing to do. The "elapsed time" option holds the time in
641  // 1/100 of second, hence we have to multiply by 10 to get a value in milliseconds.
642  OptionUint16Ptr elapsed_time = boost::dynamic_pointer_cast<
643  OptionUint16>(msg->getOption(D6O_ELAPSED_TIME));
644  auto unacked = (elapsed_time && elapsed_time->getValue() * 10 > config_->getMaxAckDelay());
645 
646  // Get the DUID of the client to see if it hasn't been recorded already.
647  OptionPtr duid = msg->getOption(D6O_CLIENTID);
648  if (!duid) {
649  return;
650  }
651 
652  bool log_unacked = false;
653 
654  // Check if the given client was already recorded.
655  auto& idx = connecting_clients_.get<0>();
656  auto existing_request = idx.find(duid->getData());
657  if (existing_request != idx.end()) {
658  // If the client was recorded and was not considered unacked
659  // but it should be considered unacked as a result of processing
660  // this packet, let's update the recorded request to mark the
661  // client unacked.
662  if (!existing_request->unacked_ && unacked) {
663  ConnectingClient6 connecting_client{ duid->getData(), unacked };
664  idx.replace(existing_request, connecting_client);
665  log_unacked = true;
666  }
667 
668  } else {
669  // This is the first time we see the packet from this client. Let's
670  // record it.
671  ConnectingClient6 connecting_client{ duid->getData(), unacked };
672  idx.insert(connecting_client);
673  log_unacked = unacked;
674 
675  if (!unacked) {
676  // This is the first time we see this client after getting into the
677  // communication interrupted state. But, this client hasn't been
678  // yet trying log enough to be considered unacked.
680  .arg(message->getLabel());
681  }
682  }
683 
684  // Only log the first time we detect a client is unacked.
685  if (log_unacked) {
686  unsigned unacked_left = 0;
687  unsigned unacked_total = connecting_clients_.get<1>().count(true);
688  if (config_->getMaxUnackedClients() >= unacked_total) {
689  unacked_left = config_->getMaxUnackedClients() - unacked_total + 1;
690  }
692  .arg(message->getLabel())
693  .arg(unacked_total)
694  .arg(unacked_left);
695  }
696 }
697 
698 bool
700  if (MultiThreadingMgr::instance().getMode()) {
701  std::lock_guard<std::mutex> lk(*mutex_);
702  return (failureDetectedInternal());
703  } else {
704  return (failureDetectedInternal());
705  }
706 }
707 
708 bool
710  return ((config_->getMaxUnackedClients() == 0) ||
711  (connecting_clients_.get<1>().count(true) >
712  config_->getMaxUnackedClients()));
713 }
714 
715 size_t
717  if (MultiThreadingMgr::instance().getMode()) {
718  std::lock_guard<std::mutex> lk(*mutex_);
719  return (connecting_clients_.size());
720  } else {
721  return (connecting_clients_.size());
722  }
723 }
724 
725 size_t
727  if (MultiThreadingMgr::instance().getMode()) {
728  std::lock_guard<std::mutex> lk(*mutex_);
729  return (connecting_clients_.get<1>().count(true));
730  } else {
731  return (connecting_clients_.get<1>().count(true));
732  }
733 }
734 
735 void
737  connecting_clients_.clear();
738 }
739 
740 } // end of namespace isc::ha
741 } // end of namespace isc
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.
Definition: macros.h:26
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.
Definition: macros.h:20
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition: option.cc:211
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
Definition: option.h:36
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.
Definition: pkt6.h:44
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
std::function< void()> heartbeat_impl_
Pointer to the function providing heartbeat implementation.
asiolink::IOServicePtr io_service_
Pointer to the common IO service instance.
STL namespace.
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
Definition: ha_messages.h:46
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
Definition: ha_messages.h:24
boost::posix_time::ptime poke_time_
Last poke time.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6
Definition: ha_messages.h:23
#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.
Definition: pkt6.h:28
boost::shared_ptr< OptionUint16 > OptionUint16Ptr
Definition: option_int.h:33
virtual bool failureDetected() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
Definition: edns.h:19
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.
Definition: pkt4.h:544
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
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.
Definition: date_time.h:59
static HttpDateTime fromRfc1123(const std::string &time_string)
Creates an instance from a string containing time value formatted as specified in RFC 1123...
Definition: date_time.cc:45
std::set< std::string > getPartnerScopes() const
Returns scopes served by the partner server.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4_UNACKED
Definition: ha_messages.h:22
void stopHeartbeat()
Stops recurring heartbeat.
This class parses and generates time values used in HTTP.
Definition: date_time.h:41
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.
Definition: pkt4.h:37
boost::posix_time::time_duration clock_skew_
Clock skew between the active servers.
const isc::log::MessageID HA_HIGH_CLOCK_SKEW_CAUSES_TERMINATION
Definition: ha_messages.h:47
CommunicationState4(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4
Definition: ha_messages.h:21
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...
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")
Definition: ha_log.h:17
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.
Definition: ha_config.h:760
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.