Kea: communication_state.cc Source File

| 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
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>
21#include <util/multi_threading_mgr.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
56CommunicationState::CommunicationState(const IOServicePtr& io_service,
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
67CommunicationState::~CommunicationState() {
68 stopHeartbeat();
69}
70
71void
72CommunicationState::modifyPokeTime(const long secs) {
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
82CommunicationState::getPartnerState() const {
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 {
104 partner_state_ = stringToState(state);
105 } catch (...) {
106 isc_throw(BadValue, "unsupported HA partner state returned "
107 << state);
108 }
109}
110
111std::set<std::string>
112CommunicationState::getPartnerScopes() const {
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
122CommunicationState::setPartnerScopes(ConstElementPtr new_scopes) {
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
154CommunicationState::startHeartbeat(const long interval,
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
206CommunicationState::stopHeartbeat() {
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
226CommunicationState::isHeartbeatRunning() const {
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
236CommunicationState::updatePokeTime() {
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
255CommunicationState::poke() {
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.
272 clearConnectingClients();
273 analyzed_messages_count_ = 0;
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
290CommunicationState::getDurationInMillisecs() const {
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
307CommunicationState::isCommunicationInterrupted() const {
308 return (getDurationInMillisecs() > config_->getMaxResponseDelay());
309}
310
311size_t
312CommunicationState::getAnalyzedMessagesCount() const {
313 return (analyzed_messages_count_);
314}
315
316bool
317CommunicationState::clockSkewShouldWarn() {
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)) {
344 last_clock_skew_warn_ = now;
345 LOG_WARN(ha_logger, HA_HIGH_CLOCK_SKEW)
346 .arg(logFormatClockSkewInternal());
347 return (true);
348 }
349 }
350
351 // The warning should not be issued.
352 return (false);
353}
354
355bool
356CommunicationState::clockSkewShouldTerminate() const {
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)) {
369 LOG_ERROR(ha_logger, HA_HIGH_CLOCK_SKEW_CAUSES_TERMINATION)
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) {
395 partner_time_at_skew_ = HttpDateTime().fromRfc1123(time_text).getPtime();
396 my_time_at_skew_ = HttpDateTime().getPtime();
397 clock_skew_ = partner_time_at_skew_ - my_time_at_skew_;
398}
399
400std::string
401CommunicationState::logFormatClockSkew() const {
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
438 ElementPtr
439CommunicationState::getReport() const {
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
476uint64_t
477CommunicationState::getUnsentUpdateCount() const {
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
487CommunicationState::increaseUnsentUpdateCount() {
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()) {
501 ++unsent_update_count_;
502 } else {
503 unsent_update_count_ = 1;
504 }
505}
506
507bool
508CommunicationState::hasPartnerNewUnsentUpdates() const {
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 &&
520 (partner_unsent_update_count_.first != partner_unsent_update_count_.second));
521}
522
523void
524CommunicationState::setPartnerUnsentUpdateCount(uint64_t unsent_update_count) {
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) {
535 partner_unsent_update_count_.first = partner_unsent_update_count_.second;
536 partner_unsent_update_count_.second = unsent_update_count;
537}
538
539CommunicationState4::CommunicationState4(const IOServicePtr& io_service,
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
562 ++analyzed_messages_count_;
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.
616 LOG_INFO(ha_logger, HA_COMMUNICATION_INTERRUPTED_CLIENT4)
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 }
628 LOG_INFO(ha_logger, HA_COMMUNICATION_INTERRUPTED_CLIENT4_UNACKED)
629 .arg(message->getLabel())
630 .arg(unacked_total)
631 .arg(unacked_left);
632 }
633}
634
635bool
636CommunicationState4::failureDetected() const {
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
646CommunicationState4::failureDetectedInternal() const {
647 return ((config_->getMaxUnackedClients() == 0) ||
648 (connecting_clients_.get<1>().count(true) >
649 config_->getMaxUnackedClients()));
650}
651
652size_t
653CommunicationState4::getConnectingClientsCount() const {
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
663CommunicationState4::getUnackedClientsCount() const {
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
673CommunicationState4::clearConnectingClients() {
674 connecting_clients_.clear();
675}
676
677CommunicationState6::CommunicationState6(const IOServicePtr& io_service,
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
700 ++analyzed_messages_count_;
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.
742 LOG_INFO(ha_logger, HA_COMMUNICATION_INTERRUPTED_CLIENT6)
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 }
754 LOG_INFO(ha_logger, HA_COMMUNICATION_INTERRUPTED_CLIENT6_UNACKED)
755 .arg(message->getLabel())
756 .arg(unacked_total)
757 .arg(unacked_left);
758 }
759}
760
761bool
762CommunicationState6::failureDetected() const {
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
772CommunicationState6::failureDetectedInternal() const {
773 return ((config_->getMaxUnackedClients() == 0) ||
774 (connecting_clients_.get<1>().count(true) >
775 config_->getMaxUnackedClients()));
776}
777
778size_t
779CommunicationState6::getConnectingClientsCount() const {
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
789CommunicationState6::getUnackedClientsCount() const {
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
799CommunicationState6::clearConnectingClients() {
800 connecting_clients_.clear();
801}
802
803} // end of namespace isc::ha
804} // end of namespace isc
boost_time_utils.h
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::asiolink::IntervalTimer
The IntervalTimer class is a wrapper for the ASIO boost::asio::deadline_timer class.
Definition: interval_timer.h:52
isc::dhcp::OptionInt
Forward declaration to OptionInt.
Definition: option_int.h:49
isc::dhcp::Option::getOption
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition: option.cc:199
isc::ha::CommunicationState4::getUnackedClientsCount
virtual size_t getUnackedClientsCount() const
Returns the current number of clients which haven't gotten a lease from the partner server.
Definition: communication_state.cc:663
isc::ha::CommunicationState4::analyzeMessageInternal
virtual void analyzeMessageInternal(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv4 message appears to be unanswered.
Definition: communication_state.cc:555
isc::ha::CommunicationState4::getConnectingClientsCount
virtual size_t getConnectingClientsCount() const
Returns the current number of clients which attempted to get a lease from the partner server.
Definition: communication_state.cc:653
isc::ha::CommunicationState4::analyzeMessage
virtual void analyzeMessage(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv4 message appears to be unanswered.
Definition: communication_state.cc:545
isc::ha::CommunicationState4::failureDetectedInternal
virtual bool failureDetectedInternal() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
Definition: communication_state.cc:646
isc::ha::CommunicationState4::connecting_clients_
ConnectingClients4 connecting_clients_
Holds information about the clients attempting to contact the partner server while the servers are in...
Definition: communication_state.h:721
isc::ha::CommunicationState4::failureDetected
virtual bool failureDetected() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
Definition: communication_state.cc:636
isc::ha::CommunicationState4::clearConnectingClients
virtual void clearConnectingClients()
Removes information about the clients the partner server should respond to while communication with t...
Definition: communication_state.cc:673
isc::ha::CommunicationState4::CommunicationState4
CommunicationState4(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
Definition: communication_state.cc:539
isc::ha::CommunicationState6::analyzeMessage
virtual void analyzeMessage(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv6 message appears to be unanswered.
Definition: communication_state.cc:683
isc::ha::CommunicationState6::connecting_clients_
ConnectingClients6 connecting_clients_
Holds information about the clients attempting to contact the partner server while the servers are in...
Definition: communication_state.h:837
isc::ha::CommunicationState6::CommunicationState6
CommunicationState6(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
Definition: communication_state.cc:677
isc::ha::CommunicationState6::clearConnectingClients
virtual void clearConnectingClients()
Removes information about the clients the partner server should respond to while communication with t...
Definition: communication_state.cc:799
isc::ha::CommunicationState6::failureDetected
virtual bool failureDetected() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
Definition: communication_state.cc:762
isc::ha::CommunicationState6::getUnackedClientsCount
virtual size_t getUnackedClientsCount() const
Returns the current number of clients which haven't gotten a lease from the partner server.
Definition: communication_state.cc:789
isc::ha::CommunicationState6::failureDetectedInternal
virtual bool failureDetectedInternal() const
Checks if the partner failure has been detected based on the DHCP traffic analysis.
Definition: communication_state.cc:772
isc::ha::CommunicationState6::analyzeMessageInternal
virtual void analyzeMessageInternal(const boost::shared_ptr< dhcp::Pkt > &message)
Checks if the DHCPv6 message appears to be unanswered.
Definition: communication_state.cc:693
isc::ha::CommunicationState6::getConnectingClientsCount
virtual size_t getConnectingClientsCount() const
Returns the current number of clients which attempted to get a lease from the partner server.
Definition: communication_state.cc:779
isc::ha::CommunicationState
Holds communication state between the two HA peers.
Definition: communication_state.h:82
isc::ha::CommunicationState::getConnectingClientsCount
virtual size_t getConnectingClientsCount() const =0
Returns the current number of clients which attempted to get a lease from the partner server.
isc::ha::CommunicationState::getUnackedClientsCount
virtual size_t getUnackedClientsCount() const =0
Returns the current number of clients which haven't got the lease from the partner server.
isc::ha::CommunicationState::clearConnectingClients
virtual void clearConnectingClients()=0
Removes information about the clients the partner server should respond to while communication with t...
isc::ha::CommunicationState::startHeartbeat
void startHeartbeat(const long interval, const std::function< void()> &heartbeat_impl)
Starts recurring heartbeat (public interface).
Definition: communication_state.cc:154
isc::ha::CommunicationState::unsent_update_count_
uint64_t unsent_update_count_
Total number of unsent lease updates.
Definition: communication_state.h:574
isc::ha::CommunicationState::isCommunicationInterrupted
bool isCommunicationInterrupted() const
Checks if communication with the partner is interrupted.
Definition: communication_state.cc:307
isc::ha::CommunicationState::setPartnerScopes
void setPartnerScopes(data::ConstElementPtr new_scopes)
Sets partner scopes.
Definition: communication_state.cc:122
isc::ha::CommunicationState::getPartnerState
int getPartnerState() const
Returns last known state of the partner.
Definition: communication_state.cc:82
isc::ha::CommunicationState::clockSkewShouldWarn
bool clockSkewShouldWarn()
Issues a warning about high clock skew between the active servers if one is warranted.
Definition: communication_state.cc:317
isc::ha::CommunicationState::logFormatClockSkew
std::string logFormatClockSkew() const
Returns current clock skew value in the logger friendly format.
Definition: communication_state.cc:401
isc::ha::CommunicationState::setPartnerUnsentUpdateCount
void setPartnerUnsentUpdateCount(uint64_t unsent_update_count)
Saves new total number of unsent lease updates from the partner.
Definition: communication_state.cc:524
isc::ha::CommunicationState::setPartnerState
void setPartnerState(const std::string &state)
Sets partner state.
Definition: communication_state.cc:92
isc::ha::CommunicationState::partner_unsent_update_count_
std::pair< uint64_t, uint64_t > partner_unsent_update_count_
Previous and current total number of unsent lease updates from the partner.
Definition: communication_state.h:582
isc::ha::CommunicationState::getPartnerScopes
std::set< std::string > getPartnerScopes() const
Returns scopes served by the partner server.
Definition: communication_state.cc:112
isc::ha::CommunicationState::~CommunicationState
virtual ~CommunicationState()
Destructor.
Definition: communication_state.cc:67
isc::ha::CommunicationState::config_
HAConfigPtr config_
High availability configuration.
Definition: communication_state.h:528
isc::ha::CommunicationState::isHeartbeatRunning
bool isHeartbeatRunning() const
Checks if recurring heartbeat is running.
Definition: communication_state.cc:226
isc::ha::CommunicationState::interval_
long interval_
Interval specified for the heartbeat.
Definition: communication_state.h:534
isc::ha::CommunicationState::stopHeartbeat
void stopHeartbeat()
Stops recurring heartbeat.
Definition: communication_state.cc:206
isc::ha::CommunicationState::increaseUnsentUpdateCount
void increaseUnsentUpdateCount()
Increases a total number of unsent lease updates by 1.
Definition: communication_state.cc:487
isc::ha::CommunicationState::setPartnerTime
void setPartnerTime(const std::string &time_text)
Provide partner's notion of time so the new clock skew can be calculated.
Definition: communication_state.cc:384
isc::ha::CommunicationState::hasPartnerNewUnsentUpdates
bool hasPartnerNewUnsentUpdates() const
Checks if the partner allocated new leases for which it hasn't sent any lease updates.
Definition: communication_state.cc:508
isc::ha::CommunicationState::io_service_
asiolink::IOServicePtr io_service_
Pointer to the common IO service instance.
Definition: communication_state.h:525
isc::ha::CommunicationState::modifyPokeTime
void modifyPokeTime(const long secs)
Modifies poke time by adding seconds to it.
Definition: communication_state.cc:72
isc::ha::CommunicationState::mutex_
const boost::scoped_ptr< std::mutex > mutex_
The mutex used to protect internal state.
Definition: communication_state.h:585
isc::ha::CommunicationState::getReport
data::ElementPtr getReport() const
Returns the report about current communication state.
Definition: communication_state.cc:439
isc::ha::CommunicationState::clock_skew_
boost::posix_time::time_duration clock_skew_
Clock skew between the active servers.
Definition: communication_state.h:551
isc::ha::CommunicationState::getAnalyzedMessagesCount
size_t getAnalyzedMessagesCount() const
Returns the number of analyzed messages while being in the communications interrupted state.
Definition: communication_state.cc:312
isc::ha::CommunicationState::analyzed_messages_count_
size_t analyzed_messages_count_
Total number of analyzed messages to be responded by partner.
Definition: communication_state.h:564
isc::ha::CommunicationState::clockSkewShouldTerminate
bool clockSkewShouldTerminate() const
Indicates whether the HA service should enter "terminated" state as a result of the clock skew exceed...
Definition: communication_state.cc:356
isc::ha::CommunicationState::heartbeat_impl_
std::function< void()> heartbeat_impl_
Pointer to the function providing heartbeat implementation.
Definition: communication_state.h:540
isc::ha::CommunicationState::poke_time_
boost::posix_time::ptime poke_time_
Last poke time.
Definition: communication_state.h:537
isc::ha::CommunicationState::updatePokeTime
boost::posix_time::time_duration updatePokeTime()
Update the poke time and compute the duration.
Definition: communication_state.cc:236
isc::ha::CommunicationState::partner_time_at_skew_
boost::posix_time::ptime partner_time_at_skew_
Partner reported time when skew was calculated.
Definition: communication_state.h:561
isc::ha::CommunicationState::CommunicationState
CommunicationState(const asiolink::IOServicePtr &io_service, const HAConfigPtr &config)
Constructor.
Definition: communication_state.cc:56
isc::ha::CommunicationState::partner_state_
int partner_state_
Last known state of the partner server.
Definition: communication_state.h:545
isc::ha::CommunicationState::last_clock_skew_warn_
boost::posix_time::ptime last_clock_skew_warn_
Holds a time when last warning about too high clock skew was issued.
Definition: communication_state.h:555
isc::ha::CommunicationState::partner_scopes_
std::set< std::string > partner_scopes_
Last known set of scopes served by the partner server.
Definition: communication_state.h:548
isc::ha::CommunicationState::getUnsentUpdateCount
uint64_t getUnsentUpdateCount() const
Returns a total number of unsent lease updates.
Definition: communication_state.cc:477
isc::ha::CommunicationState::my_time_at_skew_
boost::posix_time::ptime my_time_at_skew_
My time when skew was calculated.
Definition: communication_state.h:558
isc::ha::CommunicationState::getDurationInMillisecs
int64_t getDurationInMillisecs() const
Returns duration between the poke time and current time.
Definition: communication_state.cc:290
isc::ha::CommunicationState::timer_
asiolink::IntervalTimerPtr timer_
Interval timer triggering heartbeat commands.
Definition: communication_state.h:531
isc::ha::CommunicationState::poke
void poke()
Pokes the communication state.
Definition: communication_state.cc:255
isc::http::HttpDateTime
This class parses and generates time values used in HTTP.
Definition: date_time.h:41
isc::http::HttpDateTime::getPtime
boost::posix_time::ptime getPtime() const
Returns time encapsulated by this class.
Definition: date_time.h:59
isc::http::HttpDateTime::fromRfc1123
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
communication_state.h
data.h
date_time.h
dhcp4.h
dhcp6.h
D6O_CLIENTID
@ D6O_CLIENTID
Definition: dhcp6.h:21
D6O_ELAPSED_TIME
@ D6O_ELAPSED_TIME
Definition: dhcp6.h:28
exceptions.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:210
isc::dhcp::OptionUint16Ptr
boost::shared_ptr< OptionUint16 > OptionUint16Ptr
Definition: option_int.h:33
ha_log.h
ha_service_states.h
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
LOG_WARN
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
multi_threading_mgr.h
boost
Definition: io_service.h:14
isc::asiolink
Definition: addr_utilities.cc:170
isc::asiolink::IOServicePtr
boost::shared_ptr< IOService > IOServicePtr
Defines a smart pointer to an IOService instance.
Definition: io_service.h:110
isc::data
Definition: base_stamped_element.cc:12
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
isc::dhcp
Definition: dhcp4/client_handler.cc:20
isc::dhcp::DHO_DHCP_CLIENT_IDENTIFIER
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition: dhcp4.h:130
isc::dhcp::Pkt4Ptr
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
isc::dhcp::Pkt6Ptr
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
isc::ha::HA_HIGH_CLOCK_SKEW_CAUSES_TERMINATION
const isc::log::MessageID HA_HIGH_CLOCK_SKEW_CAUSES_TERMINATION
Definition: ha_messages.h:47
isc::ha::HA_COMMUNICATION_INTERRUPTED_CLIENT4_UNACKED
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4_UNACKED
Definition: ha_messages.h:22
isc::ha::HA_COMMUNICATION_INTERRUPTED_CLIENT6
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6
Definition: ha_messages.h:23
isc::ha::ha_logger
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
isc::ha::HAConfigPtr
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition: ha_config.h:786
isc::ha::HA_COMMUNICATION_INTERRUPTED_CLIENT6_UNACKED
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT6_UNACKED
Definition: ha_messages.h:24
isc::ha::stateToString
std::string stateToString(int state)
Returns state name.
Definition: ha_service_states.cc:14
isc::ha::HA_COMMUNICATION_INTERRUPTED_CLIENT4
const isc::log::MessageID HA_COMMUNICATION_INTERRUPTED_CLIENT4
Definition: ha_messages.h:21
isc::ha::stringToState
int stringToState(const std::string &state_name)
Returns state for a given name.
Definition: ha_service_states.cc:49
isc::ha::HA_HIGH_CLOCK_SKEW
const isc::log::MessageID HA_HIGH_CLOCK_SKEW
Definition: ha_messages.h:46
isc::http
Definition: auth_config.h:19
isc::log
Definition: buffer_appender_impl.cc:17
isc::util
Definition: edns.h:19
isc::util::ptimeToText
std::string ptimeToText(boost::posix_time::ptime t, size_t fsecs_precision=MAX_FSECS_PRECISION)
Converts ptime structure to text.
Definition: boost_time_utils.cc:14
isc
Defines the logger used by the top-level component of kea-lfc.
Definition: agent_parser.cc:143
option_int.h
pkt4.h
pkt6.h
isc::ha::CommunicationState4::ConnectingClient4
Structure holding information about the client which has send the packet being analyzed.
Definition: communication_state.h:688
isc::ha::CommunicationState4::ConnectingClient4::hwaddr_
std::vector< uint8_t > hwaddr_
Definition: communication_state.h:689
isc::ha::CommunicationState6::ConnectingClient6
Structure holding information about a client which sent a packet being analyzed.
Definition: communication_state.h:811