Kea 1.9.11
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 <sstream>
27#include <utility>
28
29using namespace isc::asiolink;
30using namespace isc::data;
31using namespace isc::dhcp;
32using namespace isc::http;
33using namespace isc::log;
34using namespace isc::util;
35
36using namespace boost::posix_time;
37using namespace std;
38
39namespace {
40
42constexpr long WARN_CLOCK_SKEW = 30;
43
45constexpr long TERM_CLOCK_SKEW = 60;
46
48constexpr long MIN_TIME_SINCE_CLOCK_SKEW_WARN = 60;
49
50}
51
52namespace isc {
53namespace ha {
54
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
67}
68
69void
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
79int
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
89void
90CommunicationState::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
99void
100CommunicationState::setPartnerStateInternal(const std::string& state) {
101 try {
103 } catch (...) {
104 isc_throw(BadValue, "unsupported HA partner state returned "
105 << state);
106 }
107}
108
109std::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
119void
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
129void
130CommunicationState::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
151void
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
162void
163CommunicationState::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
203void
205 if (MultiThreadingMgr::instance().getMode()) {
206 std::lock_guard<std::mutex> lk(*mutex_);
207 stopHeartbeatInternal();
208 } else {
209 stopHeartbeatInternal();
210 }
211}
212
213void
214CommunicationState::stopHeartbeatInternal() {
215 if (timer_) {
216 timer_->cancel();
217 timer_.reset();
218 interval_ = 0;
219 heartbeat_impl_ = 0;
220 }
221}
222
223bool
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
233boost::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
243boost::posix_time::time_duration
244CommunicationState::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
252void
254 if (MultiThreadingMgr::instance().getMode()) {
255 std::lock_guard<std::mutex> lk(*mutex_);
256 pokeInternal();
257 } else {
258 pokeInternal();
259 }
260}
261
262void
263CommunicationState::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
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
477 const HAConfigPtr& config)
478 : CommunicationState(io_service, config), connecting_clients_() {
479}
480
481void
482CommunicationState4::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
491void
492CommunicationState4::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
572bool
574 if (MultiThreadingMgr::instance().getMode()) {
575 std::lock_guard<std::mutex> lk(*mutex_);
576 return (failureDetectedInternal());
577 } else {
578 return (failureDetectedInternal());
579 }
580}
581
582bool
584 return ((config_->getMaxUnackedClients() == 0) ||
585 (connecting_clients_.get<1>().count(true) >
586 config_->getMaxUnackedClients()));
587}
588
589size_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
599size_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
609void
611 connecting_clients_.clear();
612}
613
615 const HAConfigPtr& config)
616 : CommunicationState(io_service, config), connecting_clients_() {
617}
618
619void
620CommunicationState6::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
629void
630CommunicationState6::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
698bool
700 if (MultiThreadingMgr::instance().getMode()) {
701 std::lock_guard<std::mutex> lk(*mutex_);
702 return (failureDetectedInternal());
703 } else {
704 return (failureDetectedInternal());
705 }
706}
707
708bool
710 return ((config_->getMaxUnackedClients() == 0) ||
711 (connecting_clients_.get<1>().count(true) >
712 config_->getMaxUnackedClients()));
713}
714
715size_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
725size_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
735void
737 connecting_clients_.clear();
738}
739
740} // end of namespace isc::ha
741} // 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:211
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.
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:760
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.