Kea 2.2.0
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
10#include <ha_log.h>
11#include <ha_service_states.h>
12#include <cc/data.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>
22
23#include <boost/pointer_cast.hpp>
24
25#include <functional>
26#include <limits>
27#include <sstream>
28#include <utility>
29
30using namespace isc::asiolink;
31using namespace isc::data;
32using namespace isc::dhcp;
33using namespace isc::http;
34using namespace isc::log;
35using namespace isc::util;
36
37using namespace boost::posix_time;
38using namespace std;
39
40namespace {
41
43constexpr long WARN_CLOCK_SKEW = 30;
44
46constexpr long TERM_CLOCK_SKEW = 60;
47
49constexpr long MIN_TIME_SINCE_CLOCK_SKEW_WARN = 60;
50
51}
52
53namespace isc {
54namespace ha {
55
57 const HAConfigPtr& config)
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()) {
65}
66
69}
70
71void
73 if (MultiThreadingMgr::instance().getMode()) {
74 std::lock_guard<std::mutex> lk(*mutex_);
75 poke_time_ += boost::posix_time::seconds(secs);
76 } else {
77 poke_time_ += boost::posix_time::seconds(secs);
78 }
79}
80
81int
83 if (MultiThreadingMgr::instance().getMode()) {
84 std::lock_guard<std::mutex> lk(*mutex_);
85 return (partner_state_);
86 } else {
87 return (partner_state_);
88 }
89}
90
91void
92CommunicationState::setPartnerState(const std::string& state) {
93 if (MultiThreadingMgr::instance().getMode()) {
94 std::lock_guard<std::mutex> lk(*mutex_);
95 setPartnerStateInternal(state);
96 } else {
97 setPartnerStateInternal(state);
98 }
99}
100
101void
102CommunicationState::setPartnerStateInternal(const std::string& state) {
103 try {
105 } catch (...) {
106 isc_throw(BadValue, "unsupported HA partner state returned "
107 << state);
108 }
109}
110
111std::set<std::string>
113 if (MultiThreadingMgr::instance().getMode()) {
114 std::lock_guard<std::mutex> lk(*mutex_);
115 return (partner_scopes_);
116 } else {
117 return (partner_scopes_);
118 }
119}
120
121void
123 if (MultiThreadingMgr::instance().getMode()) {
124 std::lock_guard<std::mutex> lk(*mutex_);
125 setPartnerScopesInternal(new_scopes);
126 } else {
127 setPartnerScopesInternal(new_scopes);
128 }
129}
130
131void
132CommunicationState::setPartnerScopesInternal(ConstElementPtr new_scopes) {
133 if (!new_scopes || (new_scopes->getType() != Element::list)) {
134 isc_throw(BadValue, "unable to record partner's HA scopes because"
135 " the received value is not a valid JSON list");
136 }
137
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) {
142 isc_throw(BadValue, "unable to record partner's HA scopes because"
143 " the received scope value is not a valid JSON string");
144 }
145 auto scope_str = scope->stringValue();
146 if (!scope_str.empty()) {
147 partner_scopes.insert(scope_str);
148 }
149 }
150 partner_scopes_ = partner_scopes;
151}
152
153void
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);
159 } else {
160 startHeartbeatInternal(interval, heartbeat_impl);
161 }
162}
163
164void
165CommunicationState::startHeartbeatInternal(const long interval,
166 const std::function<void()>& heartbeat_impl) {
167 bool settings_modified = false;
168
169 // If we're setting the heartbeat for the first time, it should
170 // be non-null.
171 if (heartbeat_impl) {
172 settings_modified = true;
173 heartbeat_impl_ = heartbeat_impl;
174
175 } else if (!heartbeat_impl_) {
176 // The heartbeat is re-scheduled but we have no historic implementation
177 // pointer we could re-use. This is a programmatic issue.
178 isc_throw(BadValue, "unable to start heartbeat when pointer"
179 " to the heartbeat implementation is not specified");
180 }
181
182 // If we're setting the heartbeat for the first time, the interval
183 // should be greater than 0.
184 if (interval != 0) {
185 settings_modified |= (interval_ != interval);
186 interval_ = interval;
187
188 } else if (interval_ <= 0) {
189 // The heartbeat is re-scheduled but we have no historic interval
190 // which we could re-use. This is a programmatic issue.
191 heartbeat_impl_ = 0;
192 isc_throw(BadValue, "unable to start heartbeat when interval"
193 " for the heartbeat timer is not specified");
194 }
195
196 if (!timer_) {
197 timer_.reset(new IntervalTimer(*io_service_));
198 }
199
200 if (settings_modified) {
201 timer_->setup(heartbeat_impl_, interval_, IntervalTimer::ONE_SHOT);
202 }
203}
204
205void
207 if (MultiThreadingMgr::instance().getMode()) {
208 std::lock_guard<std::mutex> lk(*mutex_);
209 stopHeartbeatInternal();
210 } else {
211 stopHeartbeatInternal();
212 }
213}
214
215void
216CommunicationState::stopHeartbeatInternal() {
217 if (timer_) {
218 timer_->cancel();
219 timer_.reset();
220 interval_ = 0;
221 heartbeat_impl_ = 0;
222 }
223}
224
225bool
227 if (MultiThreadingMgr::instance().getMode()) {
228 std::lock_guard<std::mutex> lk(*mutex_);
229 return (static_cast<bool>(timer_));
230 } else {
231 return (static_cast<bool>(timer_));
232 }
233}
234
235boost::posix_time::time_duration
237 if (MultiThreadingMgr::instance().getMode()) {
238 std::lock_guard<std::mutex> lk(*mutex_);
239 return (updatePokeTimeInternal());
240 } else {
241 return (updatePokeTimeInternal());
242 }
243}
244
245boost::posix_time::time_duration
246CommunicationState::updatePokeTimeInternal() {
247 // Remember previous poke time.
248 boost::posix_time::ptime prev_poke_time = poke_time_;
249 // Set poke time to the current time.
250 poke_time_ = boost::posix_time::microsec_clock::universal_time();
251 return (poke_time_ - prev_poke_time);
252}
253
254void
256 if (MultiThreadingMgr::instance().getMode()) {
257 std::lock_guard<std::mutex> lk(*mutex_);
258 pokeInternal();
259 } else {
260 pokeInternal();
261 }
262}
263
264void
265CommunicationState::pokeInternal() {
266 // Update poke time and compute duration.
267 boost::posix_time::time_duration duration_since_poke = updatePokeTimeInternal();
268
269 // If we have been tracking the DHCP messages directed to the partner,
270 // we need to clear any gathered information because the connection
271 // seems to be (re)established.
274
275 if (timer_) {
276 // Check the duration since last poke. If it is less than a second, we don't
277 // want to reschedule the timer. In order to avoid the overhead of
278 // re-scheduling the timer too frequently we reschedule it only if the
279 // duration is 1s or more. This matches the time resolution for heartbeats.
280 if (duration_since_poke.total_seconds() > 0) {
281 // A poke causes the timer to be re-scheduled to prevent it
282 // from triggering a heartbeat shortly after confirming the
283 // connection is ok.
284 startHeartbeatInternal();
285 }
286 }
287}
288
289int64_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
299int64_t
300CommunicationState::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
306bool
308 return (getDurationInMillisecs() > config_->getMaxResponseDelay());
309}
310
311size_t
314}
315
316bool
318 if (MultiThreadingMgr::instance().getMode()) {
319 std::lock_guard<std::mutex> lk(*mutex_);
320 return (clockSkewShouldWarnInternal());
321 } else {
322 return (clockSkewShouldWarnInternal());
323 }
324}
325
326bool
327CommunicationState::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)) {
346 .arg(logFormatClockSkewInternal());
347 return (true);
348 }
349 }
350
351 // The warning should not be issued.
352 return (false);
353}
354
355bool
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
366bool
367CommunicationState::clockSkewShouldTerminateInternal() const {
368 if (isClockSkewGreater(TERM_CLOCK_SKEW)) {
370 .arg(logFormatClockSkewInternal());
371 return (true);
372 }
373
374 return (false);
375}
376
377bool
378CommunicationState::isClockSkewGreater(const long seconds) const {
379 return ((clock_skew_.total_seconds() > seconds) ||
380 (clock_skew_.total_seconds() < -seconds));
381}
382
383void
384CommunicationState::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
393void
394CommunicationState::setPartnerTimeInternal(const std::string& time_text) {
398}
399
400std::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
410std::string
411CommunicationState::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() -
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
476uint64_t
478 if (MultiThreadingMgr::instance().getMode()) {
479 std::lock_guard<std::mutex> lk(*mutex_);
480 return (unsent_update_count_);
481 } else {
482 return (unsent_update_count_);
483 }
484}
485
486void
488 if (MultiThreadingMgr::instance().getMode()) {
489 std::lock_guard<std::mutex> lk(*mutex_);
490 increaseUnsentUpdateCountInternal();
491 } else {
492 increaseUnsentUpdateCountInternal();
493 }
494}
495
496void
497CommunicationState::increaseUnsentUpdateCountInternal() {
498 // Protect against setting the incremented value to zero.
499 // The zero value is reserved for a server startup.
500 if (unsent_update_count_ < std::numeric_limits<uint64_t>::max()) {
502 } else {
504 }
505}
506
507bool
509 if (MultiThreadingMgr::instance().getMode()) {
510 std::lock_guard<std::mutex> lk(*mutex_);
511 return (hasPartnerNewUnsentUpdatesInternal());
512 } else {
513 return (hasPartnerNewUnsentUpdatesInternal());
514 }
515}
516
517bool
518CommunicationState::hasPartnerNewUnsentUpdatesInternal() const {
519 return (partner_unsent_update_count_.second > 0 &&
521}
522
523void
525 if (MultiThreadingMgr::instance().getMode()) {
526 std::lock_guard<std::mutex> lk(*mutex_);
527 setPartnerUnsentUpdateCountInternal(unsent_update_count);
528 } else {
529 setPartnerUnsentUpdateCountInternal(unsent_update_count);
530 }
531}
532
533void
534CommunicationState::setPartnerUnsentUpdateCountInternal(uint64_t unsent_update_count) {
536 partner_unsent_update_count_.second = unsent_update_count;
537}
538
540 const HAConfigPtr& config)
541 : CommunicationState(io_service, config), connecting_clients_() {
542}
543
544void
545CommunicationState4::analyzeMessage(const boost::shared_ptr<dhcp::Pkt>& message) {
546 if (MultiThreadingMgr::instance().getMode()) {
547 std::lock_guard<std::mutex> lk(*mutex_);
548 analyzeMessageInternal(message);
549 } else {
550 analyzeMessageInternal(message);
551 }
552}
553
554void
555CommunicationState4::analyzeMessageInternal(const boost::shared_ptr<dhcp::Pkt>& message) {
556 // The DHCP message must successfully cast to a Pkt4 object.
557 Pkt4Ptr msg = boost::dynamic_pointer_cast<Pkt4>(message);
558 if (!msg) {
559 isc_throw(BadValue, "DHCP message to be analyzed is not a DHCPv4 message");
560 }
561
563
564 // Check value of the "secs" field by comparing it with the configured
565 // threshold.
566 uint16_t secs = msg->getSecs();
567
568 // It was observed that some Windows clients may send swapped bytes in the
569 // "secs" field. When the second byte is 0 and the first byte is non-zero
570 // we consider bytes to be swapped and so we correct them.
571 if ((secs > 255) && ((secs & 0xFF) == 0)) {
572 secs = ((secs >> 8) | (secs << 8));
573 }
574
575 // Check the value of the "secs" field. The "secs" field holds a value in
576 // seconds, hence we have to multiple by 1000 to get a value in milliseconds.
577 // If the secs value is above the threshold, it means that the current
578 // client should be considered unacked.
579 auto unacked = (secs * 1000 > config_->getMaxAckDelay());
580
581 // Client identifier will be stored together with the hardware address. It
582 // may remain empty if the client hasn't specified it.
583 std::vector<uint8_t> client_id;
584 OptionPtr opt_client_id = msg->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
585 if (opt_client_id) {
586 client_id = opt_client_id->getData();
587 }
588
589 bool log_unacked = false;
590
591 // Check if the given client was already recorded.
592 auto& idx = connecting_clients_.get<0>();
593 auto existing_request = idx.find(boost::make_tuple(msg->getHWAddr()->hwaddr_, client_id));
594 if (existing_request != idx.end()) {
595 // If the client was recorded and was not considered unacked
596 // but it should be considered unacked as a result of processing
597 // this packet, let's update the recorded request to mark the
598 // client unacked.
599 if (!existing_request->unacked_ && unacked) {
600 ConnectingClient4 connecting_client{ msg->getHWAddr()->hwaddr_, client_id, unacked };
601 idx.replace(existing_request, connecting_client);
602 log_unacked = true;
603 }
604
605 } else {
606 // This is the first time we see the packet from this client. Let's
607 // record it.
608 ConnectingClient4 connecting_client{ msg->getHWAddr()->hwaddr_, client_id, unacked };
609 idx.insert(connecting_client);
610 log_unacked = unacked;
611
612 if (!unacked) {
613 // This is the first time we see this client after getting into the
614 // communication interrupted state. But, this client hasn't been
615 // yet trying log enough to be considered unacked.
617 .arg(message->getLabel());
618 }
619 }
620
621 // Only log the first time we detect a client is unacked.
622 if (log_unacked) {
623 unsigned unacked_left = 0;
624 unsigned unacked_total = connecting_clients_.get<1>().count(true);
625 if (config_->getMaxUnackedClients() >= unacked_total) {
626 unacked_left = config_->getMaxUnackedClients() - unacked_total + 1;
627 }
629 .arg(message->getLabel())
630 .arg(unacked_total)
631 .arg(unacked_left);
632 }
633}
634
635bool
637 if (MultiThreadingMgr::instance().getMode()) {
638 std::lock_guard<std::mutex> lk(*mutex_);
639 return (failureDetectedInternal());
640 } else {
641 return (failureDetectedInternal());
642 }
643}
644
645bool
647 return ((config_->getMaxUnackedClients() == 0) ||
648 (connecting_clients_.get<1>().count(true) >
649 config_->getMaxUnackedClients()));
650}
651
652size_t
654 if (MultiThreadingMgr::instance().getMode()) {
655 std::lock_guard<std::mutex> lk(*mutex_);
656 return (connecting_clients_.size());
657 } else {
658 return (connecting_clients_.size());
659 }
660}
661
662size_t
664 if (MultiThreadingMgr::instance().getMode()) {
665 std::lock_guard<std::mutex> lk(*mutex_);
666 return (connecting_clients_.get<1>().count(true));
667 } else {
668 return (connecting_clients_.get<1>().count(true));
669 }
670}
671
672void
674 connecting_clients_.clear();
675}
676
678 const HAConfigPtr& config)
679 : CommunicationState(io_service, config), connecting_clients_() {
680}
681
682void
683CommunicationState6::analyzeMessage(const boost::shared_ptr<dhcp::Pkt>& message) {
684 if (MultiThreadingMgr::instance().getMode()) {
685 std::lock_guard<std::mutex> lk(*mutex_);
686 analyzeMessageInternal(message);
687 } else {
688 analyzeMessageInternal(message);
689 }
690}
691
692void
693CommunicationState6::analyzeMessageInternal(const boost::shared_ptr<dhcp::Pkt>& message) {
694 // The DHCP message must successfully cast to a Pkt6 object.
695 Pkt6Ptr msg = boost::dynamic_pointer_cast<Pkt6>(message);
696 if (!msg) {
697 isc_throw(BadValue, "DHCP message to be analyzed is not a DHCPv6 message");
698 }
699
701
702 // Check the value of the "elapsed time" option. If it is below the threshold
703 // there is nothing to do. The "elapsed time" option holds the time in
704 // 1/100 of second, hence we have to multiply by 10 to get a value in milliseconds.
705 OptionUint16Ptr elapsed_time = boost::dynamic_pointer_cast<
706 OptionUint16>(msg->getOption(D6O_ELAPSED_TIME));
707 auto unacked = (elapsed_time && elapsed_time->getValue() * 10 > config_->getMaxAckDelay());
708
709 // Get the DUID of the client to see if it hasn't been recorded already.
710 OptionPtr duid = msg->getOption(D6O_CLIENTID);
711 if (!duid) {
712 return;
713 }
714
715 bool log_unacked = false;
716
717 // Check if the given client was already recorded.
718 auto& idx = connecting_clients_.get<0>();
719 auto existing_request = idx.find(duid->getData());
720 if (existing_request != idx.end()) {
721 // If the client was recorded and was not considered unacked
722 // but it should be considered unacked as a result of processing
723 // this packet, let's update the recorded request to mark the
724 // client unacked.
725 if (!existing_request->unacked_ && unacked) {
726 ConnectingClient6 connecting_client{ duid->getData(), unacked };
727 idx.replace(existing_request, connecting_client);
728 log_unacked = true;
729 }
730
731 } else {
732 // This is the first time we see the packet from this client. Let's
733 // record it.
734 ConnectingClient6 connecting_client{ duid->getData(), unacked };
735 idx.insert(connecting_client);
736 log_unacked = unacked;
737
738 if (!unacked) {
739 // This is the first time we see this client after getting into the
740 // communication interrupted state. But, this client hasn't been
741 // yet trying log enough to be considered unacked.
743 .arg(message->getLabel());
744 }
745 }
746
747 // Only log the first time we detect a client is unacked.
748 if (log_unacked) {
749 unsigned unacked_left = 0;
750 unsigned unacked_total = connecting_clients_.get<1>().count(true);
751 if (config_->getMaxUnackedClients() >= unacked_total) {
752 unacked_left = config_->getMaxUnackedClients() - unacked_total + 1;
753 }
755 .arg(message->getLabel())
756 .arg(unacked_total)
757 .arg(unacked_left);
758 }
759}
760
761bool
763 if (MultiThreadingMgr::instance().getMode()) {
764 std::lock_guard<std::mutex> lk(*mutex_);
765 return (failureDetectedInternal());
766 } else {
767 return (failureDetectedInternal());
768 }
769}
770
771bool
773 return ((config_->getMaxUnackedClients() == 0) ||
774 (connecting_clients_.get<1>().count(true) >
775 config_->getMaxUnackedClients()));
776}
777
778size_t
780 if (MultiThreadingMgr::instance().getMode()) {
781 std::lock_guard<std::mutex> lk(*mutex_);
782 return (connecting_clients_.size());
783 } else {
784 return (connecting_clients_.size());
785 }
786}
787
788size_t
790 if (MultiThreadingMgr::instance().getMode()) {
791 std::lock_guard<std::mutex> lk(*mutex_);
792 return (connecting_clients_.get<1>().count(true));
793 } else {
794 return (connecting_clients_.get<1>().count(true));
795 }
796}
797
798void
800 connecting_clients_.clear();
801}
802
803} // end of namespace isc::ha
804} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Forward declaration to OptionInt.
Definition: option_int.h:49
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition: option.cc:199
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.
Definition: date_time.h:41
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
@ D6O_CLIENTID
Definition: dhcp6.h:21
@ D6O_ELAPSED_TIME
Definition: dhcp6.h:28
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< OptionUint16 > OptionUint16Ptr
Definition: option_int.h:33
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition: dhcp4.h:130
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const isc::log::MessageID HA_HIGH_CLOCK_SKEW_CAUSES_TERMINATION
Definition: ha_messages.h:47
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4_UNACKED
Definition: ha_messages.h:22
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6
Definition: ha_messages.h:23
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition: ha_config.h:786
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6_UNACKED
Definition: ha_messages.h:24
std::string stateToString(int state)
Returns state name.
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4
Definition: ha_messages.h:21
int stringToState(const std::string &state_name)
Returns state for a given name.
const isc::log::MessageID HA_HIGH_CLOCK_SKEW
Definition: ha_messages.h:46
Definition: edns.h:19
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.
Structure holding information about a client which sent a packet being analyzed.