Kea: ha_impl.cc Source File

| Kea 3.2.0-git

ha_impl.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2026 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 <ha_config_parser.h>
10#include <ha_impl.h>
11#include <ha_log.h>
12#include <asiolink/io_service.h>
13#include <cc/data.h>
14#include <cc/command_interpreter.h>
15#include <dhcp/pkt4.h>
16#include <dhcp/pkt6.h>
17#include <dhcpsrv/cfgmgr.h>
18#include <dhcpsrv/lease.h>
19#include <dhcpsrv/shared_network.h>
20#include <dhcpsrv/subnet.h>
21#include <stats/stats_mgr.h>
22
23using namespace isc::asiolink;
24using namespace isc::config;
25using namespace isc::data;
26using namespace isc::dhcp;
27using namespace isc::hooks;
28using namespace isc::log;
29using namespace isc::stats;
30
31namespace isc {
32namespace ha {
33
34HAImpl::HAImpl()
35 : io_service_(new IOService()), config_(), services_(new HAServiceMapper()) {
36}
37
38void
39HAImpl::configure(const ConstElementPtr& input_config) {
40 config_ = HAConfigParser::parse(input_config);
41}
42
43void
44HAImpl::startServices(const NetworkStatePtr& network_state,
45 const HAServerType& server_type) {
46 auto configs = config_->getAll();
47 for (unsigned id = 0; id < configs.size(); ++id) {
48 // Create the HA service and crank up the state machine.
49 auto service = boost::make_shared<HAService>(id, io_service_, network_state,
50 configs[id], server_type);
51 for (auto const& peer_config : configs[id]->getAllServersConfig()) {
52 services_->map(peer_config.first, service);
53 }
54 }
55 // Schedule a start of the services. This ensures we begin after
56 // the dust has settled and Kea MT mode has been firmly established.
57 io_service_->post([&]() {
58 for (auto const& service : services_->getAll()) {
59 service->startClientAndListener();
60 }
61 });
62}
63
64HAImpl::~HAImpl() {
65 stop();
66 config_.reset();
67 services_.reset(new HAServiceMapper());
68 io_service_->stopAndPoll();
69}
70
71void
72HAImpl::stop() {
73 for (auto const& service : services_->getAll()) {
74 // Shut down the services explicitly, we need finer control
75 // than relying on destruction order.
76 service->stopClientAndListener();
77 }
78 io_service_->stopAndPoll();
79}
80
81void
82HAImpl::buffer4Receive(hooks::CalloutHandle& callout_handle) {
83 // If there are multiple relationships, the HA-specific processing is
84 // in the subnet4_select hook point.
85 if (services_->hasMultiple()) {
86 return;
87 }
88
89 Pkt4Ptr query4;
90 callout_handle.getArgument("query4", query4);
91
94 try {
95 // We have to unpack the query to get access into HW address which is
96 // used to load balance the packet.
97 if (callout_handle.getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
98 query4->unpack();
99 }
100
101 } catch (const SkipRemainingOptionsError& ex) {
102 // An option failed to unpack but we are to attempt to process it
103 // anyway. Log it and let's hope for the best.
104 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC,
105 HA_BUFFER4_RECEIVE_PACKET_OPTIONS_SKIPPED)
106 .arg(ex.what());
107
108 } catch (const std::exception& ex) {
109 // Packet parsing failed. Drop the packet.
110 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_BUFFER4_RECEIVE_UNPACK_FAILED)
111 .arg(query4->getRemoteAddr().toText())
112 .arg(query4->getLocalAddr().toText())
113 .arg(query4->getIface())
114 .arg(ex.what());
115
116 // Increase the statistics of parse failures and dropped packets.
117 StatsMgr::instance().addValue("pkt4-parse-failed", static_cast<int64_t>(1));
118 StatsMgr::instance().addValue("pkt4-receive-drop", static_cast<int64_t>(1));
119 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
120 return;
121 }
122
123 // Check if we should process this query. If not, drop it.
124 if (!services_->get()->inScope(query4)) {
125 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_BUFFER4_RECEIVE_NOT_FOR_US)
126 .arg(query4->getLabel());
127 StatsMgr::instance().addValue("pkt4-not-for-us", static_cast<int64_t>(1));
128 StatsMgr::instance().addValue("pkt4-receive-drop", static_cast<int64_t>(1));
129 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
130
131 } else {
132 // We have successfully parsed the query so we have to signal
133 // to the server that it must not parse it.
134 callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
135 }
136}
137
138void
139HAImpl::subnet4Select(hooks::CalloutHandle& callout_handle) {
140 // This callout only applies in the case of multiple relationships.
141 // When there is only one relationship it has no effect because
142 // the decision if we should process the packet has been made
143 // in the buffer4_receive callout.
144 if (!services_->hasMultiple()) {
145 // Return silently. It is not an error.
146 return;
147 }
148
149 Pkt4Ptr query4;
150 callout_handle.getArgument("query4", query4);
151
152 ConstSubnet4Ptr subnet4;
153 callout_handle.getArgument("subnet4", subnet4);
154
155 // If the server failed to select the subnet this pointer is null.
156 // There is nothing we can do with this packet because we don't know
157 // which relationship it belongs to. We're even unable to check if the
158 // server is responsible for this packet.
159 if (!subnet4) {
160 // Log at debug level because that's the level at which the server
161 // logs the subnet selection failure.
162 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_SUBNET4_SELECT_NO_SUBNET_SELECTED)
163 .arg(query4->getLabel());
164 // We even do not know what is the server handling it...
165 StatsMgr::instance().addValue("pkt4-not-for-us", static_cast<int64_t>(1));
166 StatsMgr::instance().addValue("pkt4-receive-drop", static_cast<int64_t>(1));
167 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
168 return;
169 }
170
171 // The subnet configuration should contain a user context
172 // and this context should contain a mapping of the subnet to a
173 // relationship. If the context doesn't exist there is no way
174 // to determine which relationship the packet belongs to.
175 std::string server_name;
176 try {
177 server_name = HAConfig::getSubnetServerName(subnet4);
178 if (server_name.empty()) {
179 LOG_ERROR(ha_logger, HA_SUBNET4_SELECT_NO_RELATIONSHIP_SELECTOR_FOR_SUBNET)
180 .arg(query4->getLabel())
181 .arg(subnet4->toText());
182 // We even do not know what is the server handling it...
183 StatsMgr::instance().addValue("pkt4-not-for-us", static_cast<int64_t>(1));
184 StatsMgr::instance().addValue("pkt4-receive-drop", static_cast<int64_t>(1));
185 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
186 return;
187 }
188
189 } catch (...) {
190 LOG_ERROR(ha_logger, HA_SUBNET4_SELECT_INVALID_HA_SERVER_NAME)
191 .arg(query4->getLabel())
192 .arg(subnet4->toText());
193 StatsMgr::instance().addValue("pkt4-processing-failed", static_cast<int64_t>(1));
194 StatsMgr::instance().addValue("pkt4-receive-drop", static_cast<int64_t>(1));
195 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
196 return;
197 }
198
199 // Try to find a relationship matching this server name.
200 auto service = services_->get(server_name);
201 if (!service) {
202 LOG_ERROR(ha_logger, HA_SUBNET4_SELECT_NO_RELATIONSHIP_FOR_SUBNET)
203 .arg(query4->getLabel())
204 .arg(server_name);
205 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
206 StatsMgr::instance().addValue("pkt4-not-for-us", static_cast<int64_t>(1));
207 StatsMgr::instance().addValue("pkt4-receive-drop", static_cast<int64_t>(1));
208 return;
209 }
210
211 // We have found appropriate relationship. Let's see if we should
212 // process the packet. We'll drop the packet if our partner is
213 // operational and is responsible for this packet.
214 if (!service->inScope(query4)) {
215 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_SUBNET4_SELECT_NOT_FOR_US)
216 .arg(query4->getLabel())
217 .arg(server_name);
218 StatsMgr::instance().addValue("pkt4-not-for-us", static_cast<int64_t>(1));
219 StatsMgr::instance().addValue("pkt4-receive-drop", static_cast<int64_t>(1));
220 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
221 return;
222 }
223
224 // Remember the server name we retrieved from the subnet. We will
225 // need it in a leases4_committed callout that doesn't have access
226 // to the subnet object.
227 callout_handle.setContext("ha-server-name", server_name);
228}
229
230void
231HAImpl::leases4Committed(CalloutHandle& callout_handle) {
232 Pkt4Ptr query4;
233 Lease4CollectionPtr leases4;
234 Lease4CollectionPtr deleted_leases4;
235
236 // Get all arguments available for the leases4_committed hook point.
237 // If any of these arguments is not available this is a programmatic
238 // error. An exception will be thrown which will be caught by the
239 // caller and logged.
240 callout_handle.getArgument("query4", query4);
241
242 callout_handle.getArgument("leases4", leases4);
243 callout_handle.getArgument("deleted_leases4", deleted_leases4);
244
245 // In some cases we may have no leases, e.g. DHCPNAK.
246 if (leases4->empty() && deleted_leases4->empty()) {
247 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_LEASES4_COMMITTED_NOTHING_TO_UPDATE)
248 .arg(query4->getLabel());
249 return;
250 }
251
252 // Get default config and service instances.
253 HAConfigPtr config = config_->get();
254 HAServicePtr service = services_->get();
255
256 // If we have multiple relationships we need to find the one that
257 // matches our subnet.
258 if (services_->hasMultiple()) {
259 try {
260 // Retrieve the server name from the context and the respective
261 // config and service instances.
262 std::string server_name;
263 callout_handle.getContext("ha-server-name", server_name);
264 config = config_->get(server_name);
265 service = services_->get(server_name);
266
267 // This is rather impossible but let's be safe.
268 if (!config || !service) {
269 isc_throw(Unexpected, "relationship not configured for server '" << server_name << "'");
270 }
271
272 } catch (const std::exception& ex) {
273 LOG_ERROR(ha_logger, HA_LEASES4_COMMITTED_NO_RELATIONSHIP)
274 .arg(query4->getLabel())
275 .arg(ex.what());
276 StatsMgr::instance().addValue("pkt4-not-for-us", static_cast<int64_t>(1));
277 StatsMgr::instance().addValue("pkt4-receive-drop", static_cast<int64_t>(1));
278 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
279 return;
280 }
281 }
282
283 // If the hook library is configured to not send lease updates to the
284 // partner, there is nothing to do because this whole callout is
285 // currently about sending lease updates.
286 if (!config->amSendingLeaseUpdates()) {
287 // No need to log it, because it was already logged when configuration
288 // was applied.
289 return;
290 }
291
292 // Get the parking lot for this hook point. We're going to remember this
293 // pointer until we unpark the packet.
294 ParkingLotHandlePtr parking_lot = callout_handle.getParkingLotHandlePtr();
295
296 // Create a reference to the parked packet. This signals that we have a
297 // stake in unparking it.
298 parking_lot->reference(query4);
299
300 // Asynchronously send lease updates. In some cases no updates will be sent,
301 // e.g. when this server is in the partner-down state and there are no backup
302 // servers. In those cases we simply return without parking the DHCP query.
303 // The response will be sent to the client immediately.
304 try {
305 if (service->asyncSendLeaseUpdates(query4, leases4, deleted_leases4, parking_lot) == 0) {
306 // Dereference the parked packet. This releases our stake in it.
307 parking_lot->dereference(query4);
308 return;
309 }
310 } catch (...) {
311 // Make sure we dereference.
312 parking_lot->dereference(query4);
313 throw;
314 }
315
316 // The callout returns this status code to indicate to the server that it
317 // should leave the packet parked. It will be parked until each hook
318 // library with a reference, unparks the packet.
319 callout_handle.setStatus(CalloutHandle::NEXT_STEP_PARK);
320}
321
322void
323HAImpl::lease4ServerDecline(CalloutHandle& callout_handle) {
324 // Always return CONTINUE.
325 callout_handle.setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
326 size_t peers_to_update = 0;
327
328 // If the hook library is configured to not send lease updates to the
329 // partner, there is nothing to do because this whole callout is
330 // currently about sending lease updates.
331 if (!config_->get()->amSendingLeaseUpdates()) {
332 // No need to log it, because it was already logged when configuration
333 // was applied.
334 callout_handle.setArgument("peers_to_update", peers_to_update);
335 return;
336 }
337
338 // Get all arguments available for the lease4_server_decline hook point.
339 // If any of these arguments is not available this is a programmatic
340 // error. An exception will be thrown which will be caught by the
341 // caller and logged.
342 Pkt4Ptr query4;
343 callout_handle.getArgument("query4", query4);
344
345 Lease4Ptr lease4;
346 callout_handle.getArgument("lease4", lease4);
347
348 // Asynchronously send the lease update. In some cases no updates will be sent,
349 // e.g. when this server is in the partner-down state and there are no backup
350 // servers.
351 peers_to_update = services_->get()->asyncSendSingleLeaseUpdate(query4, lease4, 0);
352 callout_handle.setArgument("peers_to_update", peers_to_update);
353}
354
355void
356HAImpl::lease4Expire(CalloutHandle& callout_handle) {
357 Lease4Ptr lease4;
358 callout_handle.getArgument("lease4", lease4);
359
360 // If there are multiple relationships we need to take a detour and find
361 // a subnet the lease belongs to. The subnet will contain the information
362 // required to select appropriate HA service.
363 HAServicePtr service;
364 if (services_->hasMultiple()) {
365 auto subnet4 = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease4->subnet_id_);
366 if (!subnet4) {
367 // No subnet means that we possibly have some stale leases that don't
368 // really belong to us. Therefore, there we return early and rely on the
369 // DHCP server to reclaim them. The HA hook has no jurisdiction here.
370 return;
371 }
372
373 std::string server_name;
374 try {
375 server_name = HAConfig::getSubnetServerName(subnet4);
376 if (server_name.empty()) {
377 // Again, this subnet has no hint for HA where our lease belongs.
378 // We have to rely on the server to run reclamation of this lease.
379 return;
380 }
381 } catch (...) {
382 // Someone has tried to configure the hint for HA in the subnet but
383 // it was poorly specified. We will log an error and leave again.
384 LOG_ERROR(ha_logger, HA_LEASE4_EXPIRE_INVALID_HA_SERVER_NAME)
385 .arg(lease4->addr_.toText())
386 .arg(subnet4->toText());
387 return;
388 }
389 service = services_->get(server_name);
390
391 } else {
392 service = services_->get();
393 }
394
395 if (!service) {
396 // This is highly unlikely but better handle null pointers.
397 return;
398 }
399
400 if (!shouldReclaim(service, lease4)) {
401 // While the server is in the terminated state it has to be careful about
402 // reclaiming the leases to avoid conflicting DNS updates with a server that
403 // owns the lease. This lease apparently belongs to another server, so we
404 // should not reclaim it.
405 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_LEASE4_EXPIRE_RECLAMATION_SKIP)
406 .arg(lease4->addr_.toText());
407 callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
408 return;
409 }
410}
411
412void
413HAImpl::buffer6Receive(hooks::CalloutHandle& callout_handle) {
414 // If there are multiple relationships, the HA-specific processing is
415 // in the subnet6_select hook point.
416 if (services_->hasMultiple()) {
417 return;
418 }
419
420 Pkt6Ptr query6;
421 callout_handle.getArgument("query6", query6);
422
425 try {
426 // We have to unpack the query to get access into DUID which is
427 // used to load balance the packet.
428 if (callout_handle.getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
429 query6->unpack();
430 }
431
432 } catch (const SkipRemainingOptionsError& ex) {
433 // An option failed to unpack but we are to attempt to process it
434 // anyway. Log it and let's hope for the best.
435 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC,
436 HA_BUFFER6_RECEIVE_PACKET_OPTIONS_SKIPPED)
437 .arg(ex.what());
438
439 } catch (const std::exception& ex) {
440 // Packet parsing failed. Drop the packet.
441 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_BUFFER6_RECEIVE_UNPACK_FAILED)
442 .arg(query6->getRemoteAddr().toText())
443 .arg(query6->getLocalAddr().toText())
444 .arg(query6->getIface())
445 .arg(ex.what());
446
447 // Increase the statistics of parse failures and dropped packets.
448 StatsMgr::instance().addValue("pkt6-parse-failed", static_cast<int64_t>(1));
449 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
450
451
452 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
453 return;
454 }
455
456 // Check if we should process this query. If not, drop it.
457 if (!services_->get()->inScope(query6)) {
458 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_BUFFER6_RECEIVE_NOT_FOR_US)
459 .arg(query6->getLabel());
460 StatsMgr::instance().addValue("pkt6-not-for-us", static_cast<int64_t>(1));
461 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
462 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
463
464 } else {
465 // We have successfully parsed the query so we have to signal
466 // to the server that it must not parse it.
467 callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
468 }
469}
470
471void
472HAImpl::subnet6Select(hooks::CalloutHandle& callout_handle) {
473 // This callout only applies in the case of multiple relationships.
474 // When there is only one relationship it has no effect because
475 // the decision if we should process the packet has been made
476 // in the buffer6_receive callout.
477 if (!services_->hasMultiple()) {
478 // Return silently. It is not an error.
479 return;
480 }
481
482 Pkt6Ptr query6;
483 callout_handle.getArgument("query6", query6);
484
485 ConstSubnet6Ptr subnet6;
486 callout_handle.getArgument("subnet6", subnet6);
487
488 // If the server failed to select the subnet this pointer is null.
489 // There is nothing we can do with this packet because we don't know
490 // which relationship it belongs to. We're even unable to check if the
491 // server is responsible for this packet.
492 if (!subnet6) {
493 // Log at debug level because that's the level at which the server
494 // logs the subnet selection failure.
495 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_SUBNET6_SELECT_NO_SUBNET_SELECTED)
496 .arg(query6->getLabel());
497 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
498 // We even do not know what is the server handling it...
499 StatsMgr::instance().addValue("pkt6-not-for-us", static_cast<int64_t>(1));
500 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
501 return;
502 }
503
504 // The subnet configuration should contain a user context
505 // and this context should contain a mapping of the subnet to a
506 // relationship. If the context doesn't exist there is no way
507 // to determine which relationship the packet belongs to.
508 std::string server_name;
509 try {
510 server_name = HAConfig::getSubnetServerName(subnet6);
511 if (server_name.empty()) {
512 LOG_ERROR(ha_logger, HA_SUBNET6_SELECT_NO_RELATIONSHIP_SELECTOR_FOR_SUBNET)
513 .arg(query6->getLabel())
514 .arg(subnet6->toText());
515 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
516 // We even do not know what is the server handling it...
517 StatsMgr::instance().addValue("pkt6-not-for-us", static_cast<int64_t>(1));
518 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
519 return;
520 }
521
522 } catch (...) {
523 LOG_ERROR(ha_logger, HA_SUBNET6_SELECT_INVALID_HA_SERVER_NAME)
524 .arg(query6->getLabel())
525 .arg(subnet6->toText());
526 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
527 StatsMgr::instance().addValue("pkt6-processing-failed", static_cast<int64_t>(1));
528 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
529 return;
530 }
531
532 // Try to find a relationship matching this server name.
533 auto service = services_->get(server_name);
534 if (!service) {
535 LOG_ERROR(ha_logger, HA_SUBNET6_SELECT_NO_RELATIONSHIP_FOR_SUBNET)
536 .arg(query6->getLabel())
537 .arg(server_name);
538 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
539 StatsMgr::instance().addValue("pkt6-not-for-us", static_cast<int64_t>(1));
540 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
541 return;
542 }
543
544 // We have found appropriate relationship. Let's see if we should
545 // process the packet. We'll drop the packet if our partner is
546 // operational and is responsible for this packet.
547 if (!service->inScope(query6)) {
548 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_SUBNET6_SELECT_NOT_FOR_US)
549 .arg(query6->getLabel())
550 .arg(server_name);
551 StatsMgr::instance().addValue("pkt6-not-for-us", static_cast<int64_t>(1));
552 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
553 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
554 return;
555 }
556
557 // Remember the server name we retrieved from the subnet. We will
558 // need it in a leases4_committed callout that doesn't have access
559 // to the subnet object.
560 callout_handle.setContext("ha-server-name", server_name);
561}
562
563void
564HAImpl::leases6Committed(CalloutHandle& callout_handle) {
565 Pkt6Ptr query6;
566 Lease6CollectionPtr leases6;
567 Lease6CollectionPtr deleted_leases6;
568
569 // Get all arguments available for the leases6_committed hook point.
570 // If any of these arguments is not available this is a programmatic
571 // error. An exception will be thrown which will be caught by the
572 // caller and logged.
573 callout_handle.getArgument("query6", query6);
574
575 callout_handle.getArgument("leases6", leases6);
576 callout_handle.getArgument("deleted_leases6", deleted_leases6);
577
578 // In some cases we may have no leases.
579 if (leases6->empty() && deleted_leases6->empty()) {
580 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_LEASES6_COMMITTED_NOTHING_TO_UPDATE)
581 .arg(query6->getLabel());
582 return;
583 }
584
585 HAConfigPtr config = config_->get();
586 HAServicePtr service = services_->get();
587 if (services_->hasMultiple()) {
588 try {
589 std::string server_name;
590 callout_handle.getContext("ha-server-name", server_name);
591 config = config_->get(server_name);
592 service = services_->get(server_name);
593
594 if (!config || !service) {
595 isc_throw(Unexpected, "relationship not found for the ha-server-name='" << server_name << "'");
596 }
597
598 } catch (const std::exception& ex) {
599 LOG_ERROR(ha_logger, HA_LEASES6_COMMITTED_NO_RELATIONSHIP)
600 .arg(query6->getLabel())
601 .arg(ex.what());
602 StatsMgr::instance().addValue("pkt6-not-for-us", static_cast<int64_t>(1));
603 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
604 callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
605 return;
606 }
607 }
608
609 // If the hook library is configured to not send lease updates to the
610 // partner, there is nothing to do because this whole callout is
611 // currently about sending lease updates.
612 if (!config->amSendingLeaseUpdates()) {
613 // No need to log it, because it was already logged when configuration
614 // was applied.
615 return;
616 }
617
618 // Get the parking lot for this hook point. We're going to remember this
619 // pointer until we unpark the packet.
620 ParkingLotHandlePtr parking_lot = callout_handle.getParkingLotHandlePtr();
621
622 // Create a reference to the parked packet. This signals that we have a
623 // stake in unparking it.
624 parking_lot->reference(query6);
625
626 // Asynchronously send lease updates. In some cases no updates will be sent,
627 // e.g. when this server is in the partner-down state and there are no backup
628 // servers. In those cases we simply return without parking the DHCP query.
629 // The response will be sent to the client immediately.
630 try {
631 if (service->asyncSendLeaseUpdates(query6, leases6, deleted_leases6, parking_lot) == 0) {
632 // Dereference the parked packet. This releases our stake in it.
633 parking_lot->dereference(query6);
634 return;
635 }
636 } catch (...) {
637 // Make sure we dereference.
638 parking_lot->dereference(query6);
639 throw;
640 }
641
642 // The callout returns this status code to indicate to the server that it
643 // should leave the packet parked. It will be unparked until each hook
644 // library with a reference, unparks the packet.
645 callout_handle.setStatus(CalloutHandle::NEXT_STEP_PARK);
646}
647
648void
649HAImpl::lease6Expire(CalloutHandle& callout_handle) {
650 Lease6Ptr lease6;
651 callout_handle.getArgument("lease6", lease6);
652
653 // If there are multiple relationships we need to take a detour and find
654 // a subnet the lease belongs to. The subnet will contain the information
655 // required to select appropriate HA service.
656 HAServicePtr service;
657 if (services_->hasMultiple()) {
658 auto subnet6 = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease6->subnet_id_);
659 if (!subnet6) {
660 // No subnet means that we possibly have some stale leases that don't
661 // really belong to us. Therefore, there we return early and rely on the
662 // DHCP server to reclaim them. The HA hook has no jurisdiction here.
663 return;
664 }
665
666 std::string server_name;
667 try {
668 server_name = HAConfig::getSubnetServerName(subnet6);
669 if (server_name.empty()) {
670 // Again, this subnet has no hint for HA where our lease belongs.
671 // We have to rely on the server to run reclamation of this lease.
672 return;
673 }
674 } catch (...) {
675 // Someone has tried to configure the hint for HA in the subnet but
676 // it was poorly specified. We will log an error and leave again.
677 LOG_ERROR(ha_logger, HA_LEASE6_EXPIRE_INVALID_HA_SERVER_NAME)
678 .arg(lease6->addr_.toText())
679 .arg(subnet6->toText());
680 return;
681 }
682 service = services_->get(server_name);
683
684 } else {
685 service = services_->get();
686 }
687
688 if (!service) {
689 // This is highly unlikely but better handle null pointers.
690 return;
691 }
692
693 if (!shouldReclaim(service, lease6)) {
694 // While the server is in the terminated state it has to be careful about
695 // reclaiming the leases to avoid conflicting DNS updates with a server that
696 // owns the lease. This lease apparently belongs to another server, so we
697 // should not reclaim it.
698 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_LEASE6_EXPIRE_RECLAMATION_SKIP)
699 .arg(lease6->addr_.toText());
700 callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
701 return;
702 }
703}
704
705void
706HAImpl::commandProcessed(hooks::CalloutHandle& callout_handle) {
707 std::string command_name;
708 callout_handle.getArgument("name", command_name);
709 if (command_name == "status-get") {
710 // Get the response.
711 ConstElementPtr response;
712 callout_handle.getArgument("response", response);
713 if (!response || (response->getType() != Element::map)) {
714 return;
715 }
716 // Get the arguments item from the response.
717 ConstElementPtr resp_args = response->get("arguments");
718 if (!resp_args || (resp_args->getType() != Element::map)) {
719 return;
720 }
721 // Add the ha servers info to arguments.
722 ElementPtr mutable_resp_args =
723 boost::const_pointer_cast<Element>(resp_args);
724
725 // Process the status get command for each HA service.
726 auto ha_relationships = Element::createList();
727 for (auto const& service : services_->getAll()) {
728 auto ha_relationship = Element::createMap();
729 ConstElementPtr ha_servers = service->processStatusGet();
730 ha_relationship->set("ha-servers", ha_servers);
731 ha_relationship->set("ha-mode", Element::create(HAConfig::HAModeToString(config_->get()->getHAMode())));
732 ha_relationships->add(ha_relationship);
733 mutable_resp_args->set("high-availability", ha_relationships);
734 }
735 }
736}
737
738void
739HAImpl::heartbeatHandler(CalloutHandle& callout_handle) {
740 // Command must always be provided.
741 ConstElementPtr command;
742 callout_handle.getArgument("command", command);
743
744 // Retrieve arguments.
745 ConstElementPtr args;
746 static_cast<void>(parseCommand(args, command));
747
748 HAServicePtr service;
749 try {
750 service = getHAServiceByServerName("ha-heartbeat", args);
751
752 } catch (const std::exception& ex) {
753 // There was an error while parsing command arguments. Return an error status
754 // code to notify the user.
755 ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
756 callout_handle.setArgument("response", response);
757 return;
758 }
759
760 // Command parsing was successful, so let's process the command.
761 ConstElementPtr response = service->processHeartbeat();
762 callout_handle.setArgument("response", response);
763}
764
765void
766HAImpl::synchronizeHandler(hooks::CalloutHandle& callout_handle) {
767 // Command must always be provided.
768 ConstElementPtr command;
769 callout_handle.getArgument("command", command);
770
771 // Retrieve arguments.
772 ConstElementPtr args;
773 static_cast<void>(parseCommand(args, command));
774
775 ConstElementPtr server_name;
776 unsigned int max_period_value = 0;
777
778 HAServicePtr service;
779 try {
780 // Arguments are required for the ha-sync command.
781 if (!args) {
782 isc_throw(BadValue, "arguments not found in the 'ha-sync' command");
783 }
784
785 // Arguments must be a map.
786 if (args->getType() != Element::map) {
787 isc_throw(BadValue, "arguments in the 'ha-sync' command are not a map");
788 }
789
790 // server-name is mandatory. Otherwise how can we know the server to
791 // communicate with.
792 server_name = args->get("server-name");
793 if (!server_name) {
794 isc_throw(BadValue, "'server-name' is mandatory for the 'ha-sync' command");
795 }
796
797 // server-name must obviously be a string.
798 if (server_name->getType() != Element::string) {
799 isc_throw(BadValue, "'server-name' must be a string in the 'ha-sync' command");
800 }
801
802 // max-period is optional. In fact it is optional for dhcp-disable command too.
803 ConstElementPtr max_period = args->get("max-period");
804 if (max_period) {
805 // If it is specified, it must be a positive integer.
806 if ((max_period->getType() != Element::integer) ||
807 (max_period->intValue() <= 0)) {
808 isc_throw(BadValue, "'max-period' must be a positive integer in the 'ha-sync' command");
809 }
810
811 max_period_value = static_cast<unsigned int>(max_period->intValue());
812 }
813
814 service = getHAServiceByServerName("ha-sync", args);
815
816 } catch (const std::exception& ex) {
817 // There was an error while parsing command arguments. Return an error status
818 // code to notify the user.
819 ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
820 callout_handle.setArgument("response", response);
821 return;
822 }
823
824 // Command parsing was successful, so let's process the command.
825 ConstElementPtr response = service->processSynchronize(server_name->stringValue(),
826 max_period_value);
827 callout_handle.setArgument("response", response);
828}
829
830void
831HAImpl::scopesHandler(hooks::CalloutHandle& callout_handle) {
832 // Command must always be provided.
833 ConstElementPtr command;
834 callout_handle.getArgument("command", command);
835
836 // Retrieve arguments.
837 ConstElementPtr args;
838 static_cast<void>(parseCommand(args, command));
839
840 HAServicePtr service;
841 std::vector<std::string> scopes_vector;
842 try {
843 // Arguments must be present.
844 if (!args) {
845 isc_throw(BadValue, "arguments not found in the 'ha-scopes' command");
846 }
847
848 // Arguments must be a map.
849 if (args->getType() != Element::map) {
850 isc_throw(BadValue, "arguments in the 'ha-scopes' command are not a map");
851 }
852
853 // scopes argument is mandatory.
854 ConstElementPtr scopes = args->get("scopes");
855 if (!scopes) {
856 isc_throw(BadValue, "'scopes' is mandatory for the 'ha-scopes' command");
857 }
858
859 // It contains a list of scope names.
860 if (scopes->getType() != Element::list) {
861 isc_throw(BadValue, "'scopes' must be a list in the 'ha-scopes' command");
862 }
863
864 // Retrieve scope names from this list. The list may be empty to clear the
865 // scopes.
866 for (size_t i = 0; i < scopes->size(); ++i) {
867 ConstElementPtr scope = scopes->get(i);
868 if (!scope || scope->getType() != Element::string) {
869 isc_throw(BadValue, "scope name must be a string in the 'scopes' argument");
870 }
871 scopes_vector.push_back(scope->stringValue());
872 }
873
874 service = getHAServiceByServerName("ha-scopes", args);
875
876 } catch (const std::exception& ex) {
877 // There was an error while parsing command arguments. Return an error status
878 // code to notify the user.
879 ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
880 callout_handle.setArgument("response", response);
881 return;
882 }
883
884 // Command parsing was successful, so let's process the command.
885 ConstElementPtr response = service->processScopes(scopes_vector);
886 callout_handle.setArgument("response", response);
887}
888
889void
890HAImpl::continueHandler(hooks::CalloutHandle& callout_handle) {
891 // Command must always be provided.
892 ConstElementPtr command;
893 callout_handle.getArgument("command", command);
894
895 // Retrieve arguments.
896 ConstElementPtr args;
897 static_cast<void>(parseCommand(args, command));
898
899 HAServicePtr service;
900 try {
901 service = getHAServiceByServerName("ha-continue", args);
902
903 } catch (const std::exception& ex) {
904 // There was an error while parsing command arguments. Return an error status
905 // code to notify the user.
906 ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
907 callout_handle.setArgument("response", response);
908 return;
909 }
910 ConstElementPtr response = service->processContinue();
911 callout_handle.setArgument("response", response);
912}
913
914void
915HAImpl::maintenanceNotifyHandler(hooks::CalloutHandle& callout_handle) {
916 // Command must always be provided.
917 ConstElementPtr command;
918 callout_handle.getArgument("command", command);
919
920 HAServicePtr service;
921 try {
922 // Retrieve arguments.
923 ConstElementPtr args;
924 static_cast<void>(parseCommandWithArgs(args, command));
925
926 ConstElementPtr cancel_op = args->get("cancel");
927 if (!cancel_op) {
928 isc_throw(BadValue, "'cancel' is mandatory for the 'ha-maintenance-notify' command");
929 }
930
931 if (cancel_op->getType() != Element::boolean) {
932 isc_throw(BadValue, "'cancel' must be a boolean in the 'ha-maintenance-notify' command");
933 }
934
935 ConstElementPtr state = args->get("state");
936 if (state && state->getType() != Element::string) {
937 isc_throw(BadValue, "'state' must be a string in the 'ha-maintenance-notify' command");
938 }
939
940 service = getHAServiceByServerName("ha-maintenance-notify", args);
941
942 ConstElementPtr response = service->processMaintenanceNotify(cancel_op->boolValue(),
943 state ? state->stringValue() : "unavailable");
944 callout_handle.setArgument("response", response);
945
946 } catch (const std::exception& ex) {
947 // There was an error while parsing command arguments. Return an error status
948 // code to notify the user.
949 ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
950 callout_handle.setArgument("response", response);
951 }
952}
953
954void
955HAImpl::maintenanceStartHandler(hooks::CalloutHandle& callout_handle) {
956 ConstElementPtr response;
957 for (auto const& service : services_->getAll()) {
958 response = service->processMaintenanceStart();
959 int rcode = CONTROL_RESULT_SUCCESS;
960 static_cast<void>(parseAnswer(rcode, response));
961 if (rcode != CONTROL_RESULT_SUCCESS) {
962 break;
963 }
964 }
965 callout_handle.setArgument("response", response);
966}
967
968void
969HAImpl::maintenanceCancelHandler(hooks::CalloutHandle& callout_handle) {
970 ConstElementPtr response;
971 for (auto const& service : services_->getAll()) {
972 response = service->processMaintenanceCancel();
973 }
974 callout_handle.setArgument("response", response);
975}
976
977void
978HAImpl::haResetHandler(hooks::CalloutHandle& callout_handle) {
979 // Command must always be provided.
980 ConstElementPtr command;
981 callout_handle.getArgument("command", command);
982
983 // Retrieve arguments.
984 ConstElementPtr args;
985 static_cast<void>(parseCommand(args, command));
986
987 HAServicePtr service;
988 try {
989 service = getHAServiceByServerName("ha-reset", args);
990
991 } catch (const std::exception& ex) {
992 // There was an error while parsing command arguments. Return an error status
993 // code to notify the user.
994 ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
995 callout_handle.setArgument("response", response);
996 return;
997 }
998
999 ConstElementPtr response = service->processHAReset();
1000 callout_handle.setArgument("response", response);
1001}
1002
1003void
1004HAImpl::syncCompleteNotifyHandler(hooks::CalloutHandle& callout_handle) {
1005 // Command must always be provided.
1006 ConstElementPtr command;
1007 callout_handle.getArgument("command", command);
1008
1009 // Retrieve arguments.
1010 ConstElementPtr args;
1011 static_cast<void>(parseCommand(args, command));
1012
1013 HAServicePtr service;
1014 auto origin_id_value = NetworkState::HA_REMOTE_COMMAND+1;
1015 try {
1016 if (args) {
1017 auto origin_id = args->get("origin-id");
1018 auto origin = args->get("origin");
1019 // The origin-id is a new parameter replacing the origin. However, some versions
1020 // of Kea may still send the origin parameter instead.
1021 if (origin_id) {
1022 if (origin_id->getType() != Element::integer) {
1023 isc_throw(BadValue, "'origin-id' must be an integer in the 'ha-sync-complete-notify' command");
1024 }
1025 origin_id_value = origin_id->intValue();
1026
1027 } else if (origin) {
1028 if (origin->getType() != Element::integer) {
1029 isc_throw(BadValue, "'origin' must be an integer in the 'ha-sync-complete-notify' command");
1030 }
1031 origin_id_value = origin->intValue();
1032 }
1033 }
1034
1035 service = getHAServiceByServerName("ha-sync-complete-notify", args);
1036
1037 } catch (const std::exception& ex) {
1038 // There was an error while parsing command arguments. Return an error status
1039 // code to notify the user.
1040 ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
1041 callout_handle.setArgument("response", response);
1042 return;
1043 }
1044
1045 ConstElementPtr response = service->processSyncCompleteNotify(origin_id_value);
1046 callout_handle.setArgument("response", response);
1047}
1048
1049 HAServicePtr
1050HAImpl::getHAServiceByServerName(const std::string& command_name, ConstElementPtr args) const {
1051 HAServicePtr service;
1052 if (args) {
1053 // Arguments must be a map.
1054 if (args->getType() != Element::map) {
1055 isc_throw(BadValue, "arguments in the '" << command_name << "' command are not a map");
1056 }
1057
1058 auto server_name = args->get("server-name");
1059
1060 if (server_name) {
1061 if (server_name->getType() != Element::string) {
1062 isc_throw(BadValue, "'server-name' must be a string in the '" << command_name << "' command");
1063 }
1064 service = services_->get(server_name->stringValue());
1065 if (!service) {
1066 isc_throw(BadValue, server_name->stringValue() << " matches no configured"
1067 << " 'server-name'");
1068 }
1069 }
1070 }
1071
1072 if (!service) {
1073 service = services_->get();
1074 }
1075
1076 return (service);
1077}
1078
1079bool
1080HAImpl::shouldReclaim(const HAServicePtr& service, const dhcp::Lease4Ptr& lease4) const {
1081 return (service->shouldReclaim(lease4));
1082}
1083
1084bool
1085 HAImpl::shouldReclaim(const HAServicePtr& service, const dhcp::Lease6Ptr& lease6) const {
1086 return (service->shouldReclaim(lease6));
1087}
1088
1089} // end of namespace isc::ha
1090} // end of namespace isc
cfgmgr.h
CalloutHandle::NEXT_STEP_PARK
@ NEXT_STEP_PARK
park the packet
Definition callout_handle.h:90
CalloutHandle::NEXT_STEP_CONTINUE
@ NEXT_STEP_CONTINUE
continue normally
Definition callout_handle.h:87
CalloutHandle::NEXT_STEP_DROP
@ NEXT_STEP_DROP
drop the packet
Definition callout_handle.h:89
CalloutHandle::NEXT_STEP_SKIP
@ NEXT_STEP_SKIP
skip the next processing step
Definition callout_handle.h:88
Element::create
static ElementPtr create(const Position &pos=ZERO_POSITION())
Create a NullElement.
Definition data.cc:299
Element::map
@ map
Definition data.h:160
Element::integer
@ integer
Definition data.h:153
Element::boolean
@ boolean
Definition data.h:155
Element::list
@ list
Definition data.h:159
Element::string
@ string
Definition data.h:157
Element::createMap
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:354
Element::createList
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:349
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::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition exceptions/exceptions.cc:32
isc::Unexpected
A generic exception that is thrown when an unexpected error condition occurs.
Definition exceptions/exceptions.h:153
isc::asiolink::IOService
The IOService class is a wrapper for the ASIO io_context class.
Definition io_service.h:39
isc::dhcp::CfgMgr::instance
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
isc::dhcp::CfgMgr::getCurrentCfg
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:116
isc::dhcp::NetworkState::HA_REMOTE_COMMAND
static const unsigned int HA_REMOTE_COMMAND
The network state is being altered by a "dhcp-disable" or "dhcp-enable" command sent by a HA partner.
Definition network_state.h:95
isc::dhcp::SkipRemainingOptionsError
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition option.h:52
isc::ha::HAConfigParser::parse
static HAConfigMapperPtr parse(const data::ConstElementPtr &config)
Parses HA configuration.
Definition ha_config_parser.cc:69
isc::ha::HAConfig::getSubnetServerName
static std::string getSubnetServerName(const dhcp::ConstSubnetPtr &subnet)
Convenience function extracting a value of the ha-server-name parameter from a subnet context.
Definition ha_config.cc:524
isc::ha::HAConfig::HAModeToString
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
Definition ha_config.cc:233
isc::ha::HAImpl::stop
void stop()
Stop the client and listener.
Definition ha_impl.cc:72
isc::ha::HAImpl::scopesHandler
void scopesHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-scopes command.
Definition ha_impl.cc:831
isc::ha::HAImpl::continueHandler
void continueHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-continue command.
Definition ha_impl.cc:890
isc::ha::HAImpl::lease4Expire
void lease4Expire(hooks::CalloutHandle &callout_handle)
Implementation of the "lease4_expire" callout.
Definition ha_impl.cc:356
isc::ha::HAImpl::config_
HAConfigMapperPtr config_
Holds parsed configuration.
Definition ha_impl.h:285
isc::ha::HAImpl::syncCompleteNotifyHandler
void syncCompleteNotifyHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-sync-complete-notify command.
Definition ha_impl.cc:1004
isc::ha::HAImpl::getHAServiceByServerName
HAServicePtr getHAServiceByServerName(const std::string &command_name, data::ConstElementPtr args) const
Attempts to get an HAService by server name.
Definition ha_impl.cc:1050
isc::ha::HAImpl::services_
HAServiceMapperPtr services_
Pointer to the high availability services (state machines).
Definition ha_impl.h:288
isc::ha::HAImpl::startServices
void startServices(const dhcp::NetworkStatePtr &network_state, const HAServerType &server_type)
Creates high availability services using current configuration.
Definition ha_impl.cc:44
isc::ha::HAImpl::subnet4Select
void subnet4Select(hooks::CalloutHandle &callout_handle)
Implementation of the "subnet4_select" callout.
Definition ha_impl.cc:139
isc::ha::HAImpl::configure
void configure(const data::ConstElementPtr &input_config)
Parses configuration.
Definition ha_impl.cc:39
isc::ha::HAImpl::~HAImpl
virtual ~HAImpl()
Destructor.
Definition ha_impl.cc:64
isc::ha::HAImpl::maintenanceCancelHandler
void maintenanceCancelHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-maintenance-cancel command.
Definition ha_impl.cc:969
isc::ha::HAImpl::haResetHandler
void haResetHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-reset command.
Definition ha_impl.cc:978
isc::ha::HAImpl::maintenanceNotifyHandler
void maintenanceNotifyHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-maintenance-notify command.
Definition ha_impl.cc:915
isc::ha::HAImpl::synchronizeHandler
void synchronizeHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-sync command.
Definition ha_impl.cc:766
isc::ha::HAImpl::maintenanceStartHandler
void maintenanceStartHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-maintenance-start command.
Definition ha_impl.cc:955
isc::ha::HAImpl::lease6Expire
void lease6Expire(hooks::CalloutHandle &callout_handle)
Implementation of the "lease6_expire" callout.
Definition ha_impl.cc:649
isc::ha::HAImpl::io_service_
isc::asiolink::IOServicePtr io_service_
The hook I/O service.
Definition ha_impl.h:282
isc::ha::HAImpl::leases4Committed
void leases4Committed(hooks::CalloutHandle &callout_handle)
Implementation of the "leases4_committed" callout.
Definition ha_impl.cc:231
isc::ha::HAImpl::shouldReclaim
virtual bool shouldReclaim(const HAServicePtr &service, const dhcp::Lease4Ptr &lease4) const
Checks if the lease should be reclaimed by this server.
Definition ha_impl.cc:1080
isc::ha::HAImpl::buffer4Receive
void buffer4Receive(hooks::CalloutHandle &callout_handle)
Implementation of the "buffer4_receive" callout.
Definition ha_impl.cc:82
isc::ha::HAImpl::buffer6Receive
void buffer6Receive(hooks::CalloutHandle &callout_handle)
Implementation of the "buffer6_receive" callout.
Definition ha_impl.cc:413
isc::ha::HAImpl::commandProcessed
void commandProcessed(hooks::CalloutHandle &callout_handle)
Implementation of the "command_processed" callout.
Definition ha_impl.cc:706
isc::ha::HAImpl::HAImpl
HAImpl()
Constructor.
Definition ha_impl.cc:34
isc::ha::HAImpl::leases6Committed
void leases6Committed(hooks::CalloutHandle &callout_handle)
Implementation of the "leases6_committed" callout.
Definition ha_impl.cc:564
isc::ha::HAImpl::subnet6Select
void subnet6Select(hooks::CalloutHandle &callout_handle)
Implementation of the "subnet6_select" callout.
Definition ha_impl.cc:472
isc::ha::HAImpl::lease4ServerDecline
void lease4ServerDecline(hooks::CalloutHandle &callout_handle)
Implementation of the "lease4_server_decline" callout.
Definition ha_impl.cc:323
isc::ha::HAImpl::heartbeatHandler
void heartbeatHandler(hooks::CalloutHandle &callout_handle)
Implements handle for the heartbeat command.
Definition ha_impl.cc:739
isc::hooks::CalloutHandle
Per-packet callout handle.
Definition callout_handle.h:78
isc::hooks::CalloutHandle::getContext
void getContext(const std::string &name, T &value) const
Get context.
Definition callout_handle.h:271
isc::hooks::CalloutHandle::getParkingLotHandlePtr
ParkingLotHandlePtr getParkingLotHandlePtr() const
Returns pointer to the parking lot handle for this hook point.
Definition callout_handle.cc:75
isc::hooks::CalloutHandle::setContext
void setContext(const std::string &name, T value)
Set context.
Definition callout_handle.h:253
isc::hooks::CalloutHandle::getStatus
CalloutNextStep getStatus() const
Returns the next processing step.
Definition callout_handle.h:241
isc::hooks::CalloutHandle::setStatus
void setStatus(const CalloutNextStep next)
Sets the next processing step.
Definition callout_handle.h:231
isc::hooks::CalloutHandle::getArgument
void getArgument(const std::string &name, T &value) const
Get argument.
Definition callout_handle.h:164
isc::hooks::CalloutHandle::setArgument
void setArgument(const std::string &name, T value)
Set argument.
Definition callout_handle.h:147
isc::stats::StatsMgr::instance
static StatsMgr & instance()
Statistics Manager accessor method.
Definition lib/stats/stats_mgr.cc:30
command_interpreter.h
This file contains several functions and constants that are used for handling commands and responses ...
data.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
isc::stats::StatsMgr::addValue
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
Definition lib/stats/stats_mgr.cc:70
ha_config_parser.h
ha_impl.h
ha_log.h
io_service.h
lease.h
stats_mgr.h
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
isc::asiolink
Definition addr_utilities.cc:170
isc::config
Definition command_interpreter.cc:23
isc::config::parseAnswer
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns arguments or text status code.
Definition command_interpreter.cc:96
isc::config::CONTROL_RESULT_ERROR
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
Definition command_interpreter.h:45
isc::config::parseCommand
std::string parseCommand(ConstElementPtr &arg, ConstElementPtr command)
Parses the given command into a string containing the actual command and an ElementPtr containing the...
Definition command_interpreter.cc:200
isc::config::parseCommandWithArgs
std::string parseCommandWithArgs(ConstElementPtr &arg, ConstElementPtr command)
Parses the given command into a string containing the command name and an ElementPtr containing the m...
Definition command_interpreter.cc:238
isc::config::createAnswer
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
Definition command_interpreter.cc:54
isc::config::CONTROL_RESULT_SUCCESS
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
Definition command_interpreter.h:42
isc::data
Definition base_stamped_element.cc:12
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
isc::dhcp
Definition dhcp4/client_handler.cc:20
isc::dhcp::Lease4CollectionPtr
boost::shared_ptr< Lease4Collection > Lease4CollectionPtr
A shared pointer to the collection of IPv4 leases.
Definition lease.h:523
isc::dhcp::ConstSubnet6Ptr
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:620
isc::dhcp::ConstSubnet4Ptr
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:455
isc::dhcp::Pkt4Ptr
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:556
isc::dhcp::Lease6Ptr
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition lease.h:528
isc::dhcp::NetworkStatePtr
boost::shared_ptr< NetworkState > NetworkStatePtr
Pointer to the NetworkState object.
Definition network_state.h:220
isc::dhcp::Lease6CollectionPtr
boost::shared_ptr< Lease6Collection > Lease6CollectionPtr
A shared pointer to the collection of IPv6 leases.
Definition lease.h:696
isc::dhcp::Pkt6Ptr
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
isc::dhcp::Lease4Ptr
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition lease.h:315
isc::ha
Definition command_creator.cc:20
isc::ha::HA_BUFFER4_RECEIVE_UNPACK_FAILED
const isc::log::MessageID HA_BUFFER4_RECEIVE_UNPACK_FAILED
Definition ha_messages.h:14
isc::ha::HA_SUBNET4_SELECT_NO_SUBNET_SELECTED
const isc::log::MessageID HA_SUBNET4_SELECT_NO_SUBNET_SELECTED
Definition ha_messages.h:120
isc::ha::HA_LEASES6_COMMITTED_NOTHING_TO_UPDATE
const isc::log::MessageID HA_LEASES6_COMMITTED_NOTHING_TO_UPDATE
Definition ha_messages.h:64
isc::ha::HA_LEASES6_COMMITTED_NO_RELATIONSHIP
const isc::log::MessageID HA_LEASES6_COMMITTED_NO_RELATIONSHIP
Definition ha_messages.h:65
isc::ha::HA_BUFFER4_RECEIVE_PACKET_OPTIONS_SKIPPED
const isc::log::MessageID HA_BUFFER4_RECEIVE_PACKET_OPTIONS_SKIPPED
Definition ha_messages.h:13
isc::ha::HA_BUFFER6_RECEIVE_UNPACK_FAILED
const isc::log::MessageID HA_BUFFER6_RECEIVE_UNPACK_FAILED
Definition ha_messages.h:18
isc::ha::HAServiceMapper
HARelationshipMapper< HAService > HAServiceMapper
Type of an object mapping HAService to relationships.
Definition ha_service.h:42
isc::ha::HA_SUBNET6_SELECT_NO_SUBNET_SELECTED
const isc::log::MessageID HA_SUBNET6_SELECT_NO_SUBNET_SELECTED
Definition ha_messages.h:126
isc::ha::HA_SUBNET6_SELECT_INVALID_HA_SERVER_NAME
const isc::log::MessageID HA_SUBNET6_SELECT_INVALID_HA_SERVER_NAME
Definition ha_messages.h:122
isc::ha::HA_LEASES4_COMMITTED_NO_RELATIONSHIP
const isc::log::MessageID HA_LEASES4_COMMITTED_NO_RELATIONSHIP
Definition ha_messages.h:62
isc::ha::ha_logger
isc::log::Logger ha_logger("ha-hooks")
Definition ha_log.h:17
isc::ha::HA_SUBNET6_SELECT_NOT_FOR_US
const isc::log::MessageID HA_SUBNET6_SELECT_NOT_FOR_US
Definition ha_messages.h:123
isc::ha::HA_BUFFER6_RECEIVE_PACKET_OPTIONS_SKIPPED
const isc::log::MessageID HA_BUFFER6_RECEIVE_PACKET_OPTIONS_SKIPPED
Definition ha_messages.h:17
isc::ha::HAConfigPtr
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition ha_config.h:37
isc::ha::HA_SUBNET4_SELECT_INVALID_HA_SERVER_NAME
const isc::log::MessageID HA_SUBNET4_SELECT_INVALID_HA_SERVER_NAME
Definition ha_messages.h:116
isc::ha::HAServerType
HAServerType
Lists possible server types for which HA service is created.
Definition ha_server_type.h:14
isc::ha::HA_LEASE4_EXPIRE_INVALID_HA_SERVER_NAME
const isc::log::MessageID HA_LEASE4_EXPIRE_INVALID_HA_SERVER_NAME
Definition ha_messages.h:54
isc::ha::HA_SUBNET4_SELECT_NO_RELATIONSHIP_FOR_SUBNET
const isc::log::MessageID HA_SUBNET4_SELECT_NO_RELATIONSHIP_FOR_SUBNET
Definition ha_messages.h:118
isc::ha::HA_SUBNET4_SELECT_NOT_FOR_US
const isc::log::MessageID HA_SUBNET4_SELECT_NOT_FOR_US
Definition ha_messages.h:117
isc::ha::HA_LEASE6_EXPIRE_RECLAMATION_SKIP
const isc::log::MessageID HA_LEASE6_EXPIRE_RECLAMATION_SKIP
Definition ha_messages.h:59
isc::ha::HA_SUBNET6_SELECT_NO_RELATIONSHIP_FOR_SUBNET
const isc::log::MessageID HA_SUBNET6_SELECT_NO_RELATIONSHIP_FOR_SUBNET
Definition ha_messages.h:124
isc::ha::HA_LEASE4_EXPIRE_RECLAMATION_SKIP
const isc::log::MessageID HA_LEASE4_EXPIRE_RECLAMATION_SKIP
Definition ha_messages.h:55
isc::ha::HA_LEASES4_COMMITTED_NOTHING_TO_UPDATE
const isc::log::MessageID HA_LEASES4_COMMITTED_NOTHING_TO_UPDATE
Definition ha_messages.h:61
isc::ha::HA_SUBNET4_SELECT_NO_RELATIONSHIP_SELECTOR_FOR_SUBNET
const isc::log::MessageID HA_SUBNET4_SELECT_NO_RELATIONSHIP_SELECTOR_FOR_SUBNET
Definition ha_messages.h:119
isc::ha::HA_SUBNET6_SELECT_NO_RELATIONSHIP_SELECTOR_FOR_SUBNET
const isc::log::MessageID HA_SUBNET6_SELECT_NO_RELATIONSHIP_SELECTOR_FOR_SUBNET
Definition ha_messages.h:125
isc::ha::HA_BUFFER4_RECEIVE_NOT_FOR_US
const isc::log::MessageID HA_BUFFER4_RECEIVE_NOT_FOR_US
Definition ha_messages.h:12
isc::ha::HA_BUFFER6_RECEIVE_NOT_FOR_US
const isc::log::MessageID HA_BUFFER6_RECEIVE_NOT_FOR_US
Definition ha_messages.h:16
isc::ha::HAServicePtr
boost::shared_ptr< HAService > HAServicePtr
Pointer to the HAService class.
Definition ha_service.h:1427
isc::ha::HA_LEASE6_EXPIRE_INVALID_HA_SERVER_NAME
const isc::log::MessageID HA_LEASE6_EXPIRE_INVALID_HA_SERVER_NAME
Definition ha_messages.h:58
isc::hooks
Definition callout_handle.cc:21
isc::hooks::ParkingLotHandlePtr
boost::shared_ptr< ParkingLotHandle > ParkingLotHandlePtr
Pointer to the parking lot handle.
Definition parking_lots.h:381
isc::log
Definition buffer_appender_impl.cc:17
isc::log::DBGLVL_TRACE_BASIC
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition log_dbglevels.h:69
isc::stats
Definition context.cc:18
isc
Defines the logger used by the top-level component of kea-lfc.
Definition check_exists_add.cc:18
pkt4.h
pkt6.h
shared_network.h
subnet.h