Kea 1.5.0
dhcp6_srv.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2018 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#include <kea_version.h>
9
10#include <asiolink/io_address.h>
11#include <dhcp_ddns/ncr_msg.h>
12#include <dhcp/dhcp6.h>
14#include <dhcp/duid.h>
15#include <dhcp/duid_factory.h>
16#include <dhcp/iface_mgr.h>
17#include <dhcp/libdhcp++.h>
20#include <dhcp/option6_ia.h>
21#include <dhcp/option6_iaaddr.h>
25#include <dhcp/option_custom.h>
26#include <dhcp/option_vendor.h>
29#include <dhcp/pkt6.h>
30#include <dhcp6/dhcp6to4_ipc.h>
31#include <dhcp6/dhcp6_log.h>
32#include <dhcp6/dhcp6_srv.h>
34#include <dhcpsrv/cfgmgr.h>
35#include <dhcpsrv/lease_mgr.h>
38#include <dhcpsrv/subnet.h>
40#include <dhcpsrv/utils.h>
41#include <eval/evaluate.h>
42#include <eval/eval_messages.h>
45#include <hooks/hooks_log.h>
46#include <hooks/hooks_manager.h>
47#include <stats/stats_mgr.h>
48
49#include <util/encode/hex.h>
50#include <util/io_utilities.h>
51#include <util/pointer_util.h>
53#include <log/logger.h>
56
57#ifdef HAVE_MYSQL
59#endif
60#ifdef HAVE_PGSQL
62#endif
63#ifdef HAVE_CQL
65#endif
67
68#include <boost/bind.hpp>
69#include <boost/foreach.hpp>
70#include <boost/tokenizer.hpp>
71#include <boost/algorithm/string/erase.hpp>
72#include <boost/algorithm/string/join.hpp>
73#include <boost/algorithm/string/split.hpp>
74
75#include <algorithm>
76#include <stdlib.h>
77#include <time.h>
78#include <iomanip>
79#include <fstream>
80#include <sstream>
81
82using namespace isc;
83using namespace isc::asiolink;
84using namespace isc::cryptolink;
85using namespace isc::dhcp;
86using namespace isc::dhcp_ddns;
87using namespace isc::hooks;
88using namespace isc::log;
89using namespace isc::stats;
90using namespace isc::util;
91using namespace std;
92
93namespace {
94
96struct Dhcp6Hooks {
97 int hook_index_buffer6_receive_;
98 int hook_index_pkt6_receive_;
99 int hook_index_subnet6_select_;
100 int hook_index_leases6_committed_;
101 int hook_index_lease6_release_;
102 int hook_index_pkt6_send_;
103 int hook_index_buffer6_send_;
104 int hook_index_lease6_decline_;
105 int hook_index_host6_identifier_;
106
108 Dhcp6Hooks() {
109 hook_index_buffer6_receive_ = HooksManager::registerHook("buffer6_receive");
110 hook_index_pkt6_receive_ = HooksManager::registerHook("pkt6_receive");
111 hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
112 hook_index_leases6_committed_ = HooksManager::registerHook("leases6_committed");
113 hook_index_lease6_release_ = HooksManager::registerHook("lease6_release");
114 hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send");
115 hook_index_buffer6_send_ = HooksManager::registerHook("buffer6_send");
116 hook_index_lease6_decline_ = HooksManager::registerHook("lease6_decline");
117 hook_index_host6_identifier_ = HooksManager::registerHook("host6_identifier");
118 }
119};
120
121// Declare a Hooks object. As this is outside any function or method, it
122// will be instantiated (and the constructor run) when the module is loaded.
123// As a result, the hook indexes will be defined before any method in this
124// module is called.
125Dhcp6Hooks Hooks;
126
139createStatusCode(const Pkt6& pkt, const uint16_t status_code,
140 const std::string& status_message) {
141 Option6StatusCodePtr option_status(new Option6StatusCode(status_code,
142 status_message));
143 LOG_DEBUG(options6_logger, DBG_DHCP6_DETAIL, DHCP6_ADD_GLOBAL_STATUS_CODE)
144 .arg(pkt.getLabel())
145 .arg(option_status->dataToText());
146 return (option_status);
147}
148
164createStatusCode(const Pkt6& pkt, const Option6IA& ia, const uint16_t status_code,
165 const std::string& status_message) {
166 Option6StatusCodePtr option_status(new Option6StatusCode(status_code,
167 status_message));
168 LOG_DEBUG(options6_logger, DBG_DHCP6_DETAIL, DHCP6_ADD_STATUS_CODE_FOR_IA)
169 .arg(pkt.getLabel())
170 .arg(ia.getIAID())
171 .arg(option_status->dataToText());
172 return (option_status);
173}
174
175}; // anonymous namespace
176
177namespace isc {
178namespace dhcp {
179
180const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
181
183 : io_service_(new IOService()), port_(port), serverid_(), shutdown_(true),
184 alloc_engine_(), name_change_reqs_(),
185 network_state_(new NetworkState(NetworkState::DHCPv6))
186{
187
188 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
189
190 // Initialize objects required for DHCP server operation.
191 try {
192 // Port 0 is used for testing purposes where in most cases we don't
193 // rely on the physical interfaces. Therefore, it should be possible
194 // to create an object even when there are no usable interfaces.
195 if ((port > 0) && (IfaceMgr::instance().countIfaces() == 0)) {
196 LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
197 return;
198 }
199
200 // Create a DUID instance but do not store it into a file.
201 DUIDFactory duid_factory;
202 DuidPtr duid = duid_factory.get();
203 serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
204
205 // Instantiate allocation engine. The number of allocation attempts equal
206 // to zero indicates that the allocation engine will use the number of
207 // attempts depending on the pool size.
209
211
212 } catch (const std::exception &e) {
213 LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
214 return;
215 }
216
217 // All done, so can proceed
218 shutdown_ = false;
219}
220
223 try {
224 stopD2();
225 } catch(const std::exception& ex) {
226 // Highly unlikely, but lets Report it but go on
227 LOG_ERROR(dhcp6_logger, DHCP6_SRV_D2STOP_ERROR).arg(ex.what());
228 }
229
230 try {
232 } catch(const std::exception& ex) {
233 // Highly unlikely, but lets Report it but go on
234 // LOG_ERROR(dhcp6_logger, DHCP6_SRV_DHCP4O6_ERROR).arg(ex.what());
235 }
236
238
240
241 // Explicitly unload hooks
243}
244
246 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_SHUTDOWN_REQUEST);
247 shutdown_ = true;
248}
249
251 return (IfaceMgr::instance().receive6(timeout));
252}
253
254void Dhcpv6Srv::sendPacket(const Pkt6Ptr& packet) {
255 IfaceMgr::instance().send(packet);
256}
257
258bool
264 OptionPtr server_id = pkt->getOption(D6O_SERVERID);
265 if (server_id){
266 // Let us test received ServerID if it is same as ServerID
267 // which is being used by server
268 if (getServerID()->getData() != server_id->getData()){
270 DHCP6_PACKET_DROP_SERVERID_MISMATCH)
271 .arg(pkt->getLabel())
272 .arg(duidToString(server_id))
273 .arg(duidToString(getServerID()));
274 return (false);
275 }
276 }
277 // return True if: no serverid received or ServerIDs matching
278 return (true);
279}
280
281bool
283 switch (pkt->getType()) {
284 case DHCPV6_SOLICIT:
285 case DHCPV6_CONFIRM:
286 case DHCPV6_REBIND:
288 if (pkt->relay_info_.empty() && !pkt->getLocalAddr().isV6Multicast()) {
289 LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_DROP_UNICAST)
290 .arg(pkt->getLabel())
291 .arg(pkt->getName());
292 return (false);
293 }
294 break;
295 default:
296 // do nothing
297 ;
298 }
299 return (true);
300}
301
302void
305 bool& drop) {
306 ctx.subnet_ = selectSubnet(pkt, drop);
307 ctx.duid_ = pkt->getClientId(),
308 ctx.fwd_dns_update_ = false;
309 ctx.rev_dns_update_ = false;
310 ctx.hostname_ = "";
311 ctx.query_ = pkt;
313 ctx.hwaddr_ = getMAC(pkt);
314
315 if (drop) {
316 // Caller will immediately drop the packet so simply return now.
317 return;
318 }
319
320 // Collect host identifiers if host reservations enabled. The identifiers
321 // are stored in order of preference. The server will use them in that
322 // order to search for host reservations.
323 if (ctx.subnet_) {
324 const ConstCfgHostOperationsPtr cfg =
325 CfgMgr::instance().getCurrentCfg()->getCfgHostOperations6();
326 BOOST_FOREACH(const Host::IdentifierType& id_type,
327 cfg->getIdentifierTypes()) {
328 switch (id_type) {
329 case Host::IDENT_DUID:
330 if (ctx.duid_) {
331 ctx.addHostIdentifier(id_type, ctx.duid_->getDuid());
332 }
333 break;
334
336 if (ctx.hwaddr_) {
337 ctx.addHostIdentifier(id_type, ctx.hwaddr_->hwaddr_);
338 }
339 break;
340 case Host::IDENT_FLEX:
341 // At this point the information in the packet has been unpacked into
342 // the various packet fields and option objects has been created.
343 // Execute callouts registered for packet6_receive.
344 if (HooksManager::calloutsPresent(Hooks.hook_index_host6_identifier_)) {
345 CalloutHandlePtr callout_handle = getCalloutHandle(pkt);
346
348 std::vector<uint8_t> id;
349
350 // Use the RAII wrapper to make sure that the callout handle state is
351 // reset when this object goes out of scope. All hook points must do
352 // it to prevent possible circular dependency between the callout
353 // handle and its arguments.
354 ScopedCalloutHandleState callout_handle_state(callout_handle);
355
356 // Pass incoming packet as argument
357 callout_handle->setArgument("query6", pkt);
358 callout_handle->setArgument("id_type", type);
359 callout_handle->setArgument("id_value", id);
360
361 // Call callouts
362 HooksManager::callCallouts(Hooks.hook_index_host6_identifier_,
363 *callout_handle);
364
365 callout_handle->getArgument("id_type", type);
366 callout_handle->getArgument("id_value", id);
367
368 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_CONTINUE) &&
369 !id.empty()) {
370
372 .arg(Host::getIdentifierAsText(type, &id[0], id.size()));
373
374 ctx.addHostIdentifier(type, id);
375 }
376 }
377 break;
378 default:
379 ;
380 }
381 }
382
383 // Find host reservations using specified identifiers.
384 alloc_engine_->findReservation(ctx);
385 }
386
387 // Set KNOWN builtin class if something was found, UNKNOWN if not.
388 if (!ctx.hosts_.empty()) {
389 pkt->addClass("KNOWN");
390 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED)
391 .arg(pkt->getLabel())
392 .arg("KNOWN");
393 } else {
394 pkt->addClass("UNKNOWN");
395 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED)
396 .arg(pkt->getLabel())
397 .arg("UNKNOWN");
398 }
399
400 // Perform second pass of classification.
401 evaluateClasses(pkt, true);
402}
403
405 while (!shutdown_) {
406 try {
407 run_one();
408 getIOService()->poll();
409 } catch (const std::exception& e) {
410 // General catch-all standard exceptions that are not caught by more
411 // specific catches.
412 LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_STD_EXCEPTION)
413 .arg(e.what());
414 } catch (...) {
415 // General catch-all non-standard exception that are not caught
416 // by more specific catches.
417 LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_EXCEPTION);
418 }
419 }
420
421 return (true);
422}
423
425 // client's message and server's response
426 Pkt6Ptr query;
427 Pkt6Ptr rsp;
428
429 try {
430 // Set select() timeout to 1s. This value should not be modified
431 // because it is important that the select() returns control
432 // frequently so as the IOService can be polled for ready handlers.
433 uint32_t timeout = 1;
434 query = receivePacket(timeout);
435
436 // Log if packet has arrived. We can't log the detailed information
437 // about the DHCP message because it hasn't been unpacked/parsed
438 // yet, and it can't be parsed at this point because hooks will
439 // have to process it first. The only information available at this
440 // point are: the interface, source address and destination addresses
441 // and ports.
442 if (query) {
443 LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC, DHCP6_BUFFER_RECEIVED)
444 .arg(query->getRemoteAddr().toText())
445 .arg(query->getRemotePort())
446 .arg(query->getLocalAddr().toText())
447 .arg(query->getLocalPort())
448 .arg(query->getIface());
449
450 // Log reception of the packet. We need to increase it early, as
451 // any failures in unpacking will cause the packet to be dropped.
452 // we will increase type specific packets further down the road.
453 // See processStatsReceived().
454 StatsMgr::instance().addValue("pkt6-received", static_cast<int64_t>(1));
455
456 }
457 // We used to log that the wait was interrupted, but this is no longer
458 // the case. Our wait time is 1s now, so the lack of query packet more
459 // likely means that nothing new appeared within a second, rather than
460 // we were interrupted. And we don't want to print a message every
461 // second.
462
463
464 } catch (const SignalInterruptOnSelect&) {
465 // Packet reception interrupted because a signal has been received.
466 // This is not an error because we might have received a SIGTERM,
467 // SIGINT or SIGHUP which are handled by the server. For signals
468 // that are not handled by the server we rely on the default
469 // behavior of the system.
470 LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT_SIGNAL)
471 .arg(signal_set_->getNext());
472 } catch (const std::exception& e) {
473 LOG_ERROR(packet6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
474 }
475
476 // Handle next signal received by the process. It must be called after
477 // an attempt to receive a packet to properly handle server shut down.
478 // The SIGTERM or SIGINT will be received prior to, or during execution
479 // of select() (select is invoked by receivePacket()). When that happens,
480 // select will be interrupted. The signal handler will be invoked
481 // immediately after select(). The handler will set the shutdown flag
482 // and cause the process to terminate before the next select() function
483 // is called. If the function was called before receivePacket the
484 // process could wait up to the duration of timeout of select() to
485 // terminate.
486 try {
487 handleSignal();
488 } catch (const std::exception& e) {
489 // An (a standard or ISC) exception occurred.
490 LOG_ERROR(dhcp6_logger, DHCP6_HANDLE_SIGNAL_EXCEPTION)
491 .arg(e.what());
492 }
493
494 // Timeout may be reached or signal received, which breaks select()
495 // with no packet received
496 if (!query) {
497 return;
498 }
499
500 // If the DHCP service has been globally disabled, drop the packet.
501 if (!network_state_->isServiceEnabled()) {
503 DHCP6_PACKET_DROP_DHCP_DISABLED)
504 .arg(query->getLabel());
505 return;
506 } else {
507 processPacket(query, rsp);
508 }
509
510 if (!rsp) {
511 return;
512 }
513
514 CalloutHandlePtr callout_handle = getCalloutHandle(query);
515 processPacketBufferSend(callout_handle, rsp);
516}
517
518void
520 bool skip_unpack = false;
521
522 // The packet has just been received so contains the uninterpreted wire
523 // data; execute callouts registered for buffer6_receive.
524 if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
525 CalloutHandlePtr callout_handle = getCalloutHandle(query);
526
527 // Use the RAII wrapper to make sure that the callout handle state is
528 // reset when this object goes out of scope. All hook points must do
529 // it to prevent possible circular dependency between the callout
530 // handle and its arguments.
531 ScopedCalloutHandleState callout_handle_state(callout_handle);
532
533 // Enable copying options from the packet within hook library.
534 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
535
536 // Pass incoming packet as argument
537 callout_handle->setArgument("query6", query);
538
539 // Call callouts
540 HooksManager::callCallouts(Hooks.hook_index_buffer6_receive_, *callout_handle);
541
542 // Callouts decided to skip the next processing step. The next
543 // processing step would to parse the packet, so skip at this
544 // stage means that callouts did the parsing already, so server
545 // should skip parsing.
546 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
547 LOG_DEBUG(hooks_logger, DBG_DHCP6_DETAIL, DHCP6_HOOK_BUFFER_RCVD_SKIP)
548 .arg(query->getRemoteAddr().toText())
549 .arg(query->getLocalAddr().toText())
550 .arg(query->getIface());
551 skip_unpack = true;
552 }
553
554 // Callouts decided to drop the received packet
555 // The response (rsp) is null so the caller (run_one) will
556 // immediately return too.
557 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
558 LOG_DEBUG(hooks_logger, DBG_DHCP6_DETAIL, DHCP6_HOOK_BUFFER_RCVD_DROP)
559 .arg(query->getRemoteAddr().toText())
560 .arg(query->getLocalAddr().toText())
561 .arg(query->getIface());
562
563 // Increase the statistic of dropped packets.
564 StatsMgr::instance().addValue("pkt6-receive-drop",
565 static_cast<int64_t>(1));
566 return;
567 }
568
569 callout_handle->getArgument("query6", query);
570 }
571
572 // Unpack the packet information unless the buffer6_receive callouts
573 // indicated they did it
574 if (!skip_unpack) {
575 try {
576 LOG_DEBUG(options6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_UNPACK)
577 .arg(query->getRemoteAddr().toText())
578 .arg(query->getLocalAddr().toText())
579 .arg(query->getIface());
580 query->unpack();
581 } catch (const SkipRemainingOptionsError& e) {
582 // An option failed to unpack but we are to attempt to process it
583 // anyway. Log it and let's hope for the best.
585 DHCP6_PACKET_OPTIONS_SKIPPED)
586 .arg(e.what());
587 } catch (const std::exception &e) {
588 // Failed to parse the packet.
590 DHCP6_PACKET_DROP_PARSE_FAIL)
591 .arg(query->getRemoteAddr().toText())
592 .arg(query->getLocalAddr().toText())
593 .arg(query->getIface())
594 .arg(e.what());
595
596 // Increase the statistics of parse failures and dropped packets.
597 StatsMgr::instance().addValue("pkt6-parse-failed",
598 static_cast<int64_t>(1));
599 StatsMgr::instance().addValue("pkt6-receive-drop",
600 static_cast<int64_t>(1));
601 return;
602 }
603 }
604
605 // Update statistics accordingly for received packet.
606 processStatsReceived(query);
607
608 // Check if received query carries server identifier matching
609 // server identifier being used by the server.
610 if (!testServerID(query)) {
611
612 // Increase the statistic of dropped packets.
613 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
614 return;
615 }
616
617 // Check if the received query has been sent to unicast or multicast.
618 // The Solicit, Confirm, Rebind and Information Request will be
619 // discarded if sent to unicast address.
620 if (!testUnicast(query)) {
621
622 // Increase the statistic of dropped packets.
623 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
624 return;
625 }
626
627 // Assign this packet to a class, if possible
628 classifyPacket(query);
629
630 LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC_DATA, DHCP6_PACKET_RECEIVED)
631 .arg(query->getLabel())
632 .arg(query->getName())
633 .arg(static_cast<int>(query->getType()))
634 .arg(query->getRemoteAddr())
635 .arg(query->getLocalAddr())
636 .arg(query->getIface());
638 .arg(query->getLabel())
639 .arg(query->toText());
640
641 // At this point the information in the packet has been unpacked into
642 // the various packet fields and option objects has been created.
643 // Execute callouts registered for packet6_receive.
644 if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
645 CalloutHandlePtr callout_handle = getCalloutHandle(query);
646
647 // Use the RAII wrapper to make sure that the callout handle state is
648 // reset when this object goes out of scope. All hook points must do
649 // it to prevent possible circular dependency between the callout
650 // handle and its arguments.
651 ScopedCalloutHandleState callout_handle_state(callout_handle);
652
653 // Enable copying options from the packet within hook library.
654 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
655
656 // Pass incoming packet as argument
657 callout_handle->setArgument("query6", query);
658
659 // Call callouts
660 HooksManager::callCallouts(Hooks.hook_index_pkt6_receive_, *callout_handle);
661
662 // Callouts decided to skip the next processing step. The next
663 // processing step would to process the packet, so skip at this
664 // stage means drop.
665 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
666 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
667 LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_RCVD_SKIP)
668 .arg(query->getLabel());
669 // Increase the statistic of dropped packets.
670 StatsMgr::instance().addValue("pkt6-receive-drop",
671 static_cast<int64_t>(1));
672 return;
673 }
674
675 callout_handle->getArgument("query6", query);
676 }
677
678 // Reject the message if it doesn't pass the sanity check.
679 if (!sanityCheck(query)) {
680 return;
681 }
682
683 if (query->getType() == DHCPV6_DHCPV4_QUERY) {
684 // This call never throws. Should this change, this section must be
685 // enclosed in try-catch.
686 processDhcp4Query(query);
687 return;
688 }
689
690 // Let's create a simplified client context here.
692 bool drop = false;
693 initContext(query, ctx, drop);
694
695 // Stop here if initContext decided to drop the packet.
696 if (drop) {
697 return;
698 }
699
700 // Park point here.
701
702 try {
703 switch (query->getType()) {
704 case DHCPV6_SOLICIT:
705 rsp = processSolicit(ctx);
706 break;
707
708 case DHCPV6_REQUEST:
709 rsp = processRequest(ctx);
710 break;
711
712 case DHCPV6_RENEW:
713 rsp = processRenew(ctx);
714 break;
715
716 case DHCPV6_REBIND:
717 rsp = processRebind(ctx);
718 break;
719
720 case DHCPV6_CONFIRM:
721 rsp = processConfirm(ctx);
722 break;
723
724 case DHCPV6_RELEASE:
725 rsp = processRelease(ctx);
726 break;
727
728 case DHCPV6_DECLINE:
729 rsp = processDecline(ctx);
730 break;
731
733 rsp = processInfRequest(ctx);
734 break;
735
736 default:
737 return;
738 }
739
740 } catch (const std::exception& e) {
741
742 // Catch-all exception (at least for ones based on the isc Exception
743 // class, which covers more or less all that are explicitly raised
744 // in the Kea code), but also the standard one, which may possibly be
745 // thrown from boost code. Just log the problem and ignore the packet.
746 // (The problem is logged as a debug message because debug is
747 // disabled by default - it prevents a DDOS attack based on the
748 // sending of problem packets.)
749 LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
750 .arg(query->getName())
751 .arg(query->getRemoteAddr().toText())
752 .arg(e.what());
753
754 // Increase the statistic of dropped packets.
755 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
756 }
757
758 if (!rsp) {
759 return;
760 }
761
762 // Process relay-supplied options. It is important to call this very
763 // late in the process, because we now have all the options the
764 // server wanted to send already set. This is important, because
765 // RFC6422, section 6 states:
766 //
767 // The server SHOULD discard any options that appear in the RSOO
768 // for which it already has one or more candidates.
769 //
770 // So we ignore any RSOO options if there's an option with the same
771 // code already present.
772 processRSOO(query, rsp);
773
774 rsp->setRemoteAddr(query->getRemoteAddr());
775 rsp->setLocalAddr(query->getLocalAddr());
776
777 if (rsp->relay_info_.empty()) {
778 // Direct traffic, send back to the client directly
779 rsp->setRemotePort(DHCP6_CLIENT_PORT);
780 } else {
781 // Relayed traffic, send back to the relay agent
782 uint16_t relay_port = checkRelaySourcePort(query);
783 rsp->setRemotePort(relay_port ? relay_port : DHCP6_SERVER_PORT);
784 }
785
786 rsp->setLocalPort(DHCP6_SERVER_PORT);
787 rsp->setIndex(query->getIndex());
788 rsp->setIface(query->getIface());
789
790 bool packet_park = false;
791
792 if (!ctx.fake_allocation_ && (ctx.query_->getType() != DHCPV6_CONFIRM) &&
793 (ctx.query_->getType() != DHCPV6_INFORMATION_REQUEST) &&
794 HooksManager::calloutsPresent(Hooks.hook_index_leases6_committed_)) {
795 CalloutHandlePtr callout_handle = getCalloutHandle(query);
796
797 // Use the RAII wrapper to make sure that the callout handle state is
798 // reset when this object goes out of scope. All hook points must do
799 // it to prevent possible circular dependency between the callout
800 // handle and its arguments.
801 ScopedCalloutHandleState callout_handle_state(callout_handle);
802
803 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
804
805 // Also pass the corresponding query packet as argument
806 callout_handle->setArgument("query6", query);
807
808 Lease6CollectionPtr new_leases(new Lease6Collection());
809 if (!ctx.new_leases_.empty()) {
810 new_leases->assign(ctx.new_leases_.cbegin(),
811 ctx.new_leases_.cend());
812 }
813 callout_handle->setArgument("leases6", new_leases);
814
815 Lease6CollectionPtr deleted_leases(new Lease6Collection());
816
817 // Do per IA lists
818 for (auto const iac : ctx.ias_) {
819 if (!iac.old_leases_.empty()) {
820 for (auto old_lease : iac.old_leases_) {
821 if (ctx.new_leases_.empty()) {
822 deleted_leases->push_back(old_lease);
823 continue;
824 }
825 bool in_new = false;
826 for (auto const new_lease : ctx.new_leases_) {
827 if ((new_lease->addr_ == old_lease->addr_) &&
828 (new_lease->prefixlen_ == old_lease->prefixlen_)) {
829 in_new = true;
830 break;
831 }
832 }
833 if (!in_new) {
834 deleted_leases->push_back(old_lease);
835 }
836 }
837 }
838 }
839 callout_handle->setArgument("deleted_leases6", deleted_leases);
840
841 // Call all installed callouts
842 HooksManager::callCallouts(Hooks.hook_index_leases6_committed_,
843 *callout_handle);
844
845 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
847 DHCP6_HOOK_LEASES6_COMMITTED_DROP)
848 .arg(query->getLabel());
849 rsp.reset();
850
851 } else if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
852 packet_park = true;
853 }
854 }
855
856 if (!rsp) {
857 return;
858 }
859
860 // PARKING SPOT after leases6_committed hook point.
861 CalloutHandlePtr callout_handle = getCalloutHandle(query);
862 if (packet_park) {
864 DHCP6_HOOK_LEASES6_COMMITTED_PARK)
865 .arg(query->getLabel());
866
867 // Park the packet. The function we bind here will be executed when the hook
868 // library unparks the packet.
869 HooksManager::park("leases6_committed", query,
870 [this, callout_handle, query, rsp]() mutable {
871 processPacketPktSend(callout_handle, query, rsp);
872 processPacketBufferSend(callout_handle, rsp);
873 });
874
875 // If we have parked the packet, let's reset the pointer to the
876 // response to indicate to the caller that it should return, as
877 // the packet processing will continue via the callback.
878 rsp.reset();
879
880 } else {
881 processPacketPktSend(callout_handle, query, rsp);
882 }
883}
884
885void
887 Pkt6Ptr& query, Pkt6Ptr& rsp) {
888 if (!rsp) {
889 return;
890 }
891
892 // Specifies if server should do the packing
893 bool skip_pack = false;
894
895 // Server's reply packet now has all options and fields set.
896 // Options are represented by individual objects, but the
897 // output wire data has not been prepared yet.
898 // Execute all callouts registered for packet6_send
899 if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
900
901 // Use the RAII wrapper to make sure that the callout handle state is
902 // reset when this object goes out of scope. All hook points must do
903 // it to prevent possible circular dependency between the callout
904 // handle and its arguments.
905 ScopedCalloutHandleState callout_handle_state(callout_handle);
906
907 // Enable copying options from the packets within hook library.
908 ScopedEnableOptionsCopy<Pkt6> query_resp_options_copy(query, rsp);
909
910 // Pass incoming packet as argument
911 callout_handle->setArgument("query6", query);
912
913 // Set our response
914 callout_handle->setArgument("response6", rsp);
915
916 // Call all installed callouts
917 HooksManager::callCallouts(Hooks.hook_index_pkt6_send_, *callout_handle);
918
919 // Callouts decided to skip the next processing step. The next
920 // processing step would to pack the packet (create wire data).
921 // That step will be skipped if any callout sets skip flag.
922 // It essentially means that the callout already did packing,
923 // so the server does not have to do it again.
924 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
925 LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_SEND_SKIP)
926 .arg(rsp->getLabel());
927 skip_pack = true;
928 }
929
931 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
932 LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_SEND_DROP)
933 .arg(rsp->getLabel());
934 rsp.reset();
935 return;
936 }
937 }
938
939 if (!skip_pack) {
940 try {
941 rsp->pack();
942 } catch (const std::exception& e) {
943 LOG_ERROR(options6_logger, DHCP6_PACK_FAIL).arg(e.what());
944 return;
945 }
946
947 }
948}
949
950void
952 Pkt6Ptr& rsp) {
953 if (!rsp) {
954 return;
955 }
956
957 try {
958 // Now all fields and options are constructed into output wire buffer.
959 // Option objects modification does not make sense anymore. Hooks
960 // can only manipulate wire buffer at this stage.
961 // Let's execute all callouts registered for buffer6_send
962 if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
963
964 // Use the RAII wrapper to make sure that the callout handle state is
965 // reset when this object goes out of scope. All hook points must do
966 // it to prevent possible circular dependency between the callout
967 // handle and its arguments.
968 ScopedCalloutHandleState callout_handle_state(callout_handle);
969
970 // Enable copying options from the packet within hook library.
971 ScopedEnableOptionsCopy<Pkt6> response6_options_copy(rsp);
972
973 // Pass incoming packet as argument
974 callout_handle->setArgument("response6", rsp);
975
976 // Call callouts
977 HooksManager::callCallouts(Hooks.hook_index_buffer6_send_,
978 *callout_handle);
979
980 // Callouts decided to skip the next processing step. The next
981 // processing step would to parse the packet, so skip at this
982 // stage means drop.
983 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
984 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
986 DHCP6_HOOK_BUFFER_SEND_SKIP)
987 .arg(rsp->getLabel());
988 return;
989 }
990
991 callout_handle->getArgument("response6", rsp);
992 }
993
994 LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_RESPONSE_DATA)
995 .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
996
997 sendPacket(rsp);
998
999 // Update statistics accordingly for sent packet.
1000 processStatsSent(rsp);
1001
1002 } catch (const std::exception& e) {
1003 LOG_ERROR(packet6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
1004 }
1005}
1006
1007std::string
1009 stringstream tmp;
1010
1011 OptionBuffer data = opt->getData();
1012
1013 bool colon = false;
1014 for (OptionBufferConstIter it = data.begin(); it != data.end(); ++it) {
1015 if (colon) {
1016 tmp << ":";
1017 }
1018 tmp << hex << setw(2) << setfill('0') << static_cast<uint16_t>(*it);
1019 if (!colon) {
1020 colon = true;
1021 }
1022 }
1023
1024 return tmp.str();
1025}
1026
1027void
1029 // Add client-id.
1030 OptionPtr clientid = question->getOption(D6O_CLIENTID);
1031 if (clientid) {
1032 answer->addOption(clientid);
1033 }
1035
1036 // If this is a relayed message, we need to copy relay information
1037 if (!question->relay_info_.empty()) {
1038 answer->copyRelayInfo(question);
1039 }
1040
1041}
1042
1043void
1045 const CfgOptionList&) {
1046 // add server-id
1047 answer->addOption(getServerID());
1048}
1049
1050void
1053 CfgOptionList& co_list) {
1054 // Firstly, host specific options.
1055 if (ctx.currentHost() && !ctx.currentHost()->getCfgOption6()->empty()) {
1056 co_list.push_back(ctx.currentHost()->getCfgOption6());
1057 }
1058
1059 // Secondly, pool specific options. Pools are defined within a subnet, so
1060 // if there is no subnet, there is nothing to do.
1061 if (ctx.subnet_) {
1062 BOOST_FOREACH(const AllocEngine::ResourceType& resource,
1064 PoolPtr pool = ctx.subnet_->getPool(resource.second == 128 ?
1066 resource.first, false);
1067 if (pool && !pool->getCfgOption()->empty()) {
1068 co_list.push_back(pool->getCfgOption());
1069 }
1070 }
1071 };
1072
1073 if (ctx.subnet_) {
1074 // Next, subnet configured options.
1075 if (!ctx.subnet_->getCfgOption()->empty()) {
1076 co_list.push_back(ctx.subnet_->getCfgOption());
1077 }
1078
1079 // Then, shared network specific options.
1080 SharedNetwork6Ptr network;
1081 ctx.subnet_->getSharedNetwork(network);
1082 if (network && !network->getCfgOption()->empty()) {
1083 co_list.push_back(network->getCfgOption());
1084 }
1085 }
1086
1087 // Each class in the incoming packet
1088 const ClientClasses& classes = question->getClasses();
1089 for (ClientClasses::const_iterator cclass = classes.cbegin();
1090 cclass != classes.cend(); ++cclass) {
1091 // Find the client class definition for this class
1093 getClientClassDictionary()->findClass(*cclass);
1094 if (!ccdef) {
1095 // Not found: the class is built-in or not configured
1096 if (!isClientClassBuiltIn(*cclass)) {
1097 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_UNCONFIGURED)
1098 .arg(question->getLabel())
1099 .arg(*cclass);
1100 }
1101 // Skip it
1102 continue;
1103 }
1104
1105 if (ccdef->getCfgOption()->empty()) {
1106 // Skip classes which don't configure options
1107 continue;
1108 }
1109
1110 co_list.push_back(ccdef->getCfgOption());
1111 }
1112
1113 // Last global options
1114 if (!CfgMgr::instance().getCurrentCfg()->getCfgOption()->empty()) {
1115 co_list.push_back(CfgMgr::instance().getCurrentCfg()->getCfgOption());
1116 }
1117}
1118
1119void
1121 const CfgOptionList& co_list) {
1122
1123 // Unlikely short cut
1124 if (co_list.empty()) {
1125 return;
1126 }
1127
1128 std::vector<uint16_t> requested_opts;
1129
1130 // Client requests some options using ORO option. Try to
1131 // get this option from client's message.
1132 boost::shared_ptr<OptionIntArray<uint16_t> > option_oro =
1133 boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >
1134 (question->getOption(D6O_ORO));
1135
1136 // Get the list of options that client requested.
1137 if (option_oro) {
1138 requested_opts = option_oro->getValues();
1139 }
1140 // Iterate on the configured option list to add persistent options
1141 for (CfgOptionList::const_iterator copts = co_list.begin();
1142 copts != co_list.end(); ++copts) {
1143 const OptionContainerPtr& opts = (*copts)->getAll(DHCP6_OPTION_SPACE);
1144 if (!opts) {
1145 continue;
1146 }
1147 // Get persistent options
1148 const OptionContainerPersistIndex& idx = opts->get<2>();
1149 const OptionContainerPersistRange& range = idx.equal_range(true);
1150 for (OptionContainerPersistIndex::const_iterator desc = range.first;
1151 desc != range.second; ++desc) {
1152 // Add the persistent option code to requested options
1153 requested_opts.push_back(desc->option_->getType());
1154 }
1155 }
1156
1157 BOOST_FOREACH(uint16_t opt, requested_opts) {
1158 // Iterate on the configured option list
1159 for (CfgOptionList::const_iterator copts = co_list.begin();
1160 copts != co_list.end(); ++copts) {
1161 OptionDescriptor desc = (*copts)->get(DHCP6_OPTION_SPACE, opt);
1162 // Got it: add it and jump to the outer loop
1163 if (desc.option_) {
1164 answer->addOption(desc.option_);
1165 break;
1166 }
1167 }
1168 }
1169}
1170
1171void
1173 Pkt6Ptr& answer,
1175 const CfgOptionList& co_list) {
1176
1177 // Leave if there is no subnet matching the incoming packet.
1178 // There is no need to log the error message here because
1179 // it will be logged in the assignLease() when it fails to
1180 // pick the suitable subnet. We don't want to duplicate
1181 // error messages in such case.
1182 if (!ctx.subnet_) {
1183 return;
1184 }
1185
1186 // Try to get the vendor option
1187 boost::shared_ptr<OptionVendor> vendor_req =
1188 boost::dynamic_pointer_cast<OptionVendor>(question->getOption(D6O_VENDOR_OPTS));
1189 if (!vendor_req || co_list.empty()) {
1190 return;
1191 }
1192
1193 uint32_t vendor_id = vendor_req->getVendorId();
1194 std::vector<uint16_t> requested_opts;
1195
1196 // Let's try to get ORO within that vendor-option
1199 boost::shared_ptr<OptionUint16Array> oro =
1200 boost::dynamic_pointer_cast<OptionUint16Array>(vendor_req->getOption(DOCSIS3_V6_ORO));
1201 if (oro) {
1202 requested_opts = oro->getValues();
1203 }
1204 // Iterate on the configured option list to add persistent options
1205 for (CfgOptionList::const_iterator copts = co_list.begin();
1206 copts != co_list.end(); ++copts) {
1207 const OptionContainerPtr& opts = (*copts)->getAll(vendor_id);
1208 if (!opts) {
1209 continue;
1210 }
1211 // Get persistent options
1212 const OptionContainerPersistIndex& idx = opts->get<2>();
1213 const OptionContainerPersistRange& range = idx.equal_range(true);
1214 for (OptionContainerPersistIndex::const_iterator desc = range.first;
1215 desc != range.second; ++desc) {
1216 // Add the persistent option code to requested options
1217 requested_opts.push_back(desc->option_->getType());
1218 }
1219 }
1220
1221 // If there is nothing to add don't do anything then.
1222 if (requested_opts.empty()) {
1223 return;
1224 }
1225
1226 boost::shared_ptr<OptionVendor> vendor_rsp(new OptionVendor(Option::V6, vendor_id));
1227
1228 // Get the list of options that client requested.
1229 bool added = false;
1230
1231 BOOST_FOREACH(uint16_t opt, requested_opts) {
1232 for (CfgOptionList::const_iterator copts = co_list.begin();
1233 copts != co_list.end(); ++copts) {
1234 OptionDescriptor desc = (*copts)->get(vendor_id, opt);
1235 if (desc.option_) {
1236 vendor_rsp->addOption(desc.option_);
1237 added = true;
1238 break;
1239 }
1240 }
1241 }
1242
1243 if (added) {
1244 answer->addOption(vendor_rsp);
1245 }
1246}
1247
1248bool
1250 try {
1251 switch (pkt->getType()) {
1252 case DHCPV6_SOLICIT:
1253 case DHCPV6_REBIND:
1254 case DHCPV6_CONFIRM:
1256 return (true);
1257
1258 case DHCPV6_REQUEST:
1259 case DHCPV6_RENEW:
1260 case DHCPV6_RELEASE:
1261 case DHCPV6_DECLINE:
1263 return (true);
1264
1268 return (true);
1269
1270 default:
1272 DHCP6_UNKNOWN_MSG_RECEIVED)
1273 .arg(static_cast<int>(pkt->getType()))
1274 .arg(pkt->getIface());
1275 }
1276
1277 } catch (const RFCViolation& e) {
1278 LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
1279 .arg(pkt->getName())
1280 .arg(pkt->getRemoteAddr().toText())
1281 .arg(e.what());
1282
1283 }
1284
1285 // Increase the statistic of dropped packets.
1286 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
1287 return (false);
1288}
1289
1290void
1292 RequirementLevel serverid) {
1293 OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
1294 switch (clientid) {
1295 case MANDATORY: {
1296 if (client_ids.size() != 1) {
1297 isc_throw(RFCViolation, "Exactly 1 client-id option expected in "
1298 << pkt->getName() << ", but " << client_ids.size()
1299 << " received");
1300 }
1301 sanityCheckDUID(client_ids.begin()->second, "client-id");
1302 break;
1303 }
1304 case OPTIONAL:
1305 if (client_ids.size() > 1) {
1306 isc_throw(RFCViolation, "Too many (" << client_ids.size()
1307 << ") client-id options received in " << pkt->getName());
1308 }
1309 if (!client_ids.empty()) {
1310 sanityCheckDUID(client_ids.begin()->second, "client-id");
1311 }
1312 break;
1313
1314 case FORBIDDEN:
1315 // doesn't make sense - client-id is always allowed
1316 break;
1317 }
1318
1319 OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
1320 switch (serverid) {
1321 case FORBIDDEN:
1322 if (!server_ids.empty()) {
1323 isc_throw(RFCViolation, "Server-id option was not expected, but "
1324 << server_ids.size() << " received in " << pkt->getName());
1325 }
1326 break;
1327
1328 case MANDATORY:
1329 if (server_ids.size() != 1) {
1330 isc_throw(RFCViolation, "Invalid number of server-id options received ("
1331 << server_ids.size() << "), exactly 1 expected in message "
1332 << pkt->getName());
1333 }
1334 sanityCheckDUID(server_ids.begin()->second, "server-id");
1335 break;
1336
1337 case OPTIONAL:
1338 if (server_ids.size() > 1) {
1339 isc_throw(RFCViolation, "Too many (" << server_ids.size()
1340 << ") server-id options received in " << pkt->getName());
1341 }
1342 if (!server_ids.empty()) {
1343 sanityCheckDUID(server_ids.begin()->second, "server-id");
1344 }
1345 }
1346}
1347
1348void Dhcpv6Srv::sanityCheckDUID(const OptionPtr& opt, const std::string& opt_name) {
1349 if (!opt) {
1350 isc_throw(RFCViolation, "Unable to find expected option " << opt_name);
1351 }
1352
1353 // The client-id or server-id has to have at least 3 bytes of useful data:
1354 // two for duid type and one more for actual duid value.
1355 uint16_t len = opt->len() - opt->getHeaderLen();
1356 if (len < 3) {
1357 isc_throw(RFCViolation, "Received empty or truncated " << opt_name << " option: "
1358 << len << " byte(s) only");
1359 }
1360}
1361
1363Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question, bool& drop) {
1364 const SubnetSelector& selector = CfgSubnets6::initSelector(question);
1365
1367 getCfgSubnets6()->selectSubnet(selector);
1368
1369 // Let's execute all callouts registered for subnet6_receive
1370 if (HooksManager::calloutsPresent(Hooks.hook_index_subnet6_select_)) {
1371 CalloutHandlePtr callout_handle = getCalloutHandle(question);
1372
1373 // Use the RAII wrapper to make sure that the callout handle state is
1374 // reset when this object goes out of scope. All hook points must do
1375 // it to prevent possible circular dependency between the callout
1376 // handle and its arguments.
1377 ScopedCalloutHandleState callout_handle_state(callout_handle);
1378
1379 // Enable copying options from the packet within hook library.
1380 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(question);
1381
1382 // Set new arguments
1383 callout_handle->setArgument("query6", question);
1384 callout_handle->setArgument("subnet6", subnet);
1385
1386 // We pass pointer to const collection for performance reasons.
1387 // Otherwise we would get a non-trivial performance penalty each
1388 // time subnet6_select is called.
1389 callout_handle->setArgument("subnet6collection",
1390 CfgMgr::instance().getCurrentCfg()->
1391 getCfgSubnets6()->getAll());
1392
1393 // Call user (and server-side) callouts
1394 HooksManager::callCallouts(Hooks.hook_index_subnet6_select_, *callout_handle);
1395
1396 // Callouts decided to skip this step. This means that no
1397 // subnet will be selected. Packet processing will continue,
1398 // but it will be severely limited (i.e. only global options
1399 // will be assigned)
1400 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
1401 LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_SUBNET6_SELECT_SKIP)
1402 .arg(question->getLabel());
1403 return (Subnet6Ptr());
1404 }
1405
1406 // Callouts decided to drop the packet. It is a superset of the
1407 // skip case so no subnet will be selected.
1408 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1409 LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_SUBNET6_SELECT_DROP)
1410 .arg(question->getLabel());
1411 drop = true;
1412 return (Subnet6Ptr());
1413 }
1414
1415 // Use whatever subnet was specified by the callout
1416 callout_handle->getArgument("subnet6", subnet);
1417 }
1418
1419 if (subnet) {
1420 // Log at higher debug level that subnet has been found.
1421 LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC_DATA, DHCP6_SUBNET_SELECTED)
1422 .arg(question->getLabel())
1423 .arg(subnet->getID());
1424 // Log detailed information about the selected subnet at the
1425 // lower debug level.
1427 .arg(question->getLabel())
1428 .arg(subnet->toText());
1429
1430 } else {
1431 LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_SUBNET_SELECTION_FAILED)
1432 .arg(question->getLabel());
1433 }
1434
1435 return (subnet);
1436}
1437
1438void
1439Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
1441
1442 Subnet6Ptr subnet = ctx.subnet_;
1443
1444 // We need to allocate addresses for all IA_NA options in the client's
1445 // question (i.e. SOLICIT or REQUEST) message.
1446 // @todo add support for IA_TA
1447
1448 // For the lease allocation it is critical that the client has sent
1449 // DUID. There is no need to check for the presence of the DUID here
1450 // because we have already checked it in the sanityCheck().
1451
1452 // Now that we have all information about the client, let's iterate over all
1453 // received options and handle IA_NA options one by one and store our
1454 // responses in answer message (ADVERTISE or REPLY).
1455 //
1456 // @todo: IA_TA once we implement support for temporary addresses.
1457 for (OptionCollection::iterator opt = question->options_.begin();
1458 opt != question->options_.end(); ++opt) {
1459 switch (opt->second->getType()) {
1460 case D6O_IA_NA: {
1461 OptionPtr answer_opt = assignIA_NA(question, answer, ctx,
1462 boost::dynamic_pointer_cast<
1463 Option6IA>(opt->second));
1464 if (answer_opt) {
1465 answer->addOption(answer_opt);
1466 }
1467 break;
1468 }
1469 case D6O_IA_PD: {
1470 OptionPtr answer_opt = assignIA_PD(question, answer, ctx,
1471 boost::dynamic_pointer_cast<
1472 Option6IA>(opt->second));
1473 if (answer_opt) {
1474 answer->addOption(answer_opt);
1475 }
1476 }
1477 default:
1478 break;
1479 }
1480 }
1481
1482 // Subnet may be modified by the allocation engine, if the initial subnet
1483 // belongs to a shared network.
1484 if (ctx.subnet_ && subnet && (subnet->getID() != ctx.subnet_->getID())) {
1485 SharedNetwork6Ptr network;
1486 subnet->getSharedNetwork(network);
1487 if (network) {
1488 LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC_DATA, DHCP6_SUBNET_DYNAMICALLY_CHANGED)
1489 .arg(question->getLabel())
1490 .arg(subnet->toText())
1491 .arg(ctx.subnet_->toText())
1492 .arg(network->getName());
1493 }
1494 }
1495}
1496
1497
1498void
1499Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
1502
1503 // Get Client FQDN Option from the client's message. If this option hasn't
1504 // been included, do nothing.
1505 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
1506 Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
1507 if (!fqdn) {
1508 D2ClientConfig::ReplaceClientNameMode replace_name_mode =
1509 d2_mgr.getD2ClientConfig()->getReplaceClientNameMode();
1510 if (d2_mgr.ddnsEnabled() &&
1511 (replace_name_mode == D2ClientConfig::RCM_ALWAYS ||
1512 replace_name_mode == D2ClientConfig::RCM_WHEN_NOT_PRESENT)) {
1513 // Fabricate an empty "client" FQDN with flags requesting
1514 // the server do all the updates. The flags will get modified
1515 // below according the configuration options, the name will
1516 // be supplied later on.
1519 LOG_DEBUG(ddns6_logger, DBG_DHCP6_DETAIL, DHCP6_DDNS_GENERATE_FQDN)
1520 .arg(question->getLabel());
1521 } else {
1522 // No FQDN so get the lease hostname from the host reservation if
1523 // there is one.
1524 if (ctx.currentHost()) {
1525 ctx.hostname_ = ctx.currentHost()->getHostname();
1526 }
1527
1528 return;
1529 }
1530 }
1531
1532 LOG_DEBUG(ddns6_logger, DBG_DHCP6_DETAIL, DHCP6_DDNS_RECEIVE_FQDN)
1533 .arg(question->getLabel())
1534 .arg(fqdn->toText());
1535
1536 // Create the DHCPv6 Client FQDN Option to be included in the server's
1537 // response to a client.
1538 Option6ClientFqdnPtr fqdn_resp(new Option6ClientFqdn(*fqdn));
1539
1540 // Set the server S, N, and O flags based on client's flags and
1541 // current configuration.
1542 d2_mgr.adjustFqdnFlags<Option6ClientFqdn>(*fqdn, *fqdn_resp);
1543
1544 // If there's a reservation and it has a hostname specified, use it!
1545 if (ctx.currentHost() && !ctx.currentHost()->getHostname().empty()) {
1546 // Add the qualifying suffix.
1547 // After #3765, this will only occur if the suffix is not empty.
1548 fqdn_resp->setDomainName(d2_mgr.qualifyName(ctx.currentHost()->getHostname(),
1549 true),
1551 } else {
1552 // Adjust the domain name based on domain name value and type sent by
1553 // the client and current configuration.
1554 d2_mgr.adjustDomainName<Option6ClientFqdn>(*fqdn, *fqdn_resp);
1555 }
1556
1557 // Once we have the FQDN setup to use it for the lease hostname. This
1558 // only gets replaced later if the FQDN is to be generated from the address.
1559 ctx.hostname_ = fqdn_resp->getDomainName();
1560
1561 // The FQDN has been processed successfully. Let's append it to the
1562 // response to be sent to a client. Note that the Client FQDN option is
1563 // always sent back to the client if Client FQDN was included in the
1564 // client's message.
1565 LOG_DEBUG(ddns6_logger, DBG_DHCP6_DETAIL, DHCP6_DDNS_RESPONSE_FQDN_DATA)
1566 .arg(question->getLabel())
1567 .arg(fqdn_resp->toText());
1568 answer->addOption(fqdn_resp);
1569}
1570
1571void
1574 // Don't create NameChangeRequests if DNS updates are disabled.
1575 if (!CfgMgr::instance().ddnsEnabled()) {
1576 return;
1577 }
1578
1579 // The response message instance is always required. For instance it
1580 // holds the Client Identifier. It is a programming error if supplied
1581 // message is NULL.
1582 if (!answer) {
1583 isc_throw(isc::Unexpected, "an instance of the object"
1584 << " encapsulating server's message must not be"
1585 << " NULL when creating DNS NameChangeRequest");
1586 }
1587
1588 // It is likely that client haven't included the FQDN option. In such case,
1589 // FQDN option will be NULL. This is valid state, so we simply return.
1590 Option6ClientFqdnPtr opt_fqdn = boost::dynamic_pointer_cast<
1591 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
1592 if (!opt_fqdn) {
1593 return;
1594 }
1595
1596 // Get the update directions that should be performed based on our
1597 // response FQDN flags.
1598 bool do_fwd = false;
1599 bool do_rev = false;
1601 do_fwd, do_rev);
1602 if (!do_fwd && !do_rev) {
1603 // Flags indicate there is Nothing to do, get out now.
1604 // The Most likely scenario is that we are honoring the client's
1605 // request that no updates be done.
1606 return;
1607 }
1608
1609 // Get the Client Id. It is mandatory and a function creating a response
1610 // would have thrown an exception if it was missing. Thus throwing
1611 // Unexpected if it is missing as it is a programming error.
1612 OptionPtr opt_duid = answer->getOption(D6O_CLIENTID);
1613 if (!opt_duid) {
1615 "client identifier is required when creating a new"
1616 " DNS NameChangeRequest");
1617 }
1618 DuidPtr duid = DuidPtr(new DUID(opt_duid->getData()));
1619
1620 // Get the FQDN in the on-wire format. It will be needed to compute
1621 // DHCID.
1622 OutputBuffer name_buf(1);
1623 opt_fqdn->packDomainName(name_buf);
1624 const uint8_t* name_data = static_cast<const uint8_t*>(name_buf.getData());
1625 // @todo currently D2Dhcid accepts a vector holding FQDN.
1626 // However, it will be faster if we used a pointer name_data.
1627 std::vector<uint8_t> buf_vec(name_data, name_data + name_buf.getLength());
1628 // Compute DHCID from Client Identifier and FQDN.
1629 isc::dhcp_ddns::D2Dhcid dhcid(*duid, buf_vec);
1630
1631 // Get all IAs from the answer. For each IA, holding an address we will
1632 // create a corresponding NameChangeRequest.
1633 OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
1634 for (OptionCollection::const_iterator answer_ia =
1635 answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
1638 Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
1639 Option6IAAddr>(answer_ia->second->getOption(D6O_IAADDR));
1640 // We need an address to create a name-to-address mapping.
1641 // If address is missing for any reason, go to the next IA.
1642 if (!iaaddr) {
1643 continue;
1644 }
1645
1646 // If the lease for iaaddr is in the list of changed leases, we need
1647 // to determine if the changes included changes to the FQDN. If there
1648 // were changes to the FQDN then we need to update DNS, otherwise
1649 // we do not.
1650 bool extended_only = false;
1651 for (Lease6Collection::const_iterator l = ctx.currentIA().changed_leases_.begin();
1652 l != ctx.currentIA().changed_leases_.end(); ++l) {
1653 if ((*l)->addr_ == iaaddr->getAddress()) {
1654 if ((*l)->hostname_ == opt_fqdn->getDomainName() &&
1655 (*l)->fqdn_fwd_ == do_fwd && (*l)->fqdn_rev_ == do_rev) {
1656 extended_only = true;
1657 break;
1658 }
1659 }
1660 }
1661
1662 if (extended_only) {
1663 continue;
1664 }
1665
1666 // Create new NameChangeRequest. Use the domain name from the FQDN.
1667 // This is an FQDN included in the response to the client, so it
1668 // holds a fully qualified domain-name already (not partial).
1669 // Get the IP address from the lease.
1672 do_fwd, do_rev,
1673 opt_fqdn->getDomainName(),
1674 iaaddr->getAddress().toText(),
1675 dhcid, 0, iaaddr->getValid()));
1676
1678 DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr->toText());
1679
1680 // Post the NCR to the D2ClientMgr.
1682
1687 return;
1688 }
1689}
1690
1693 CfgMACSources mac_sources = CfgMgr::instance().getCurrentCfg()->
1694 getMACSources().get();
1695 HWAddrPtr hwaddr;
1696 for (CfgMACSources::const_iterator it = mac_sources.begin();
1697 it != mac_sources.end(); ++it) {
1698 hwaddr = pkt->getMAC(*it);
1699 if (hwaddr) {
1700 return (hwaddr);
1701 }
1702 }
1703 return (hwaddr);
1704}
1705
1707Dhcpv6Srv::assignIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
1709 boost::shared_ptr<Option6IA> ia) {
1710
1711 // Check if the client sent us a hint in his IA_NA. Clients may send an
1712 // address in their IA_NA options as a suggestion (e.g. the last address
1713 // they used before).
1714 Option6IAAddrPtr hint_opt =
1715 boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
1717 if (hint_opt) {
1718 hint = hint_opt->getAddress();
1719 }
1720
1721 LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
1722 .arg(query->getLabel())
1723 .arg(ia->getIAID())
1724 .arg(hint_opt ? hint.toText() : "(no hint)");
1725
1726 // convenience values
1727 const Subnet6Ptr& subnet = ctx.subnet_;
1728
1729 // If there is no subnet selected for handling this IA_NA, the only thing left to do is
1730 // to say that we are sorry, but the user won't get an address. As a convenience, we
1731 // use a different status text to indicate that (compare to the same status code,
1732 // but different wording below)
1733 if (!subnet) {
1734 // Create an empty IA_NA option with IAID matching the request.
1735 // Note that we don't use OptionDefinition class to create this option.
1736 // This is because we prefer using a constructor of Option6IA that
1737 // initializes IAID. Otherwise we would have to use setIAID() after
1738 // creation of the option which has some performance implications.
1739 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
1740
1741 // Insert status code NoAddrsAvail.
1742 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoAddrsAvail,
1743 "Server could not select subnet for"
1744 " this client"));
1745 return (ia_rsp);
1746 }
1747
1748 // Get DDNS update direction flags
1749 bool do_fwd = false;
1750 bool do_rev = false;
1751 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
1752 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
1753 if (fqdn) {
1755 do_rev);
1756 }
1757
1758 // Update per-packet context values.
1759 ctx.fwd_dns_update_ = do_fwd;
1760 ctx.rev_dns_update_ = do_rev;
1761
1762 // Set per-IA context values.
1763 ctx.createIAContext();
1764 ctx.currentIA().iaid_ = ia->getIAID();
1765 ctx.currentIA().addHint(hint);
1767
1768 // Use allocation engine to pick a lease for this client. Allocation engine
1769 // will try to honor the hint, but it is just a hint - some other address
1770 // may be used instead. If fake_allocation is set to false, the lease will
1771 // be inserted into the LeaseMgr as well.
1772 Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
1773
1775 Lease6Ptr lease;
1776 if (!leases.empty()) {
1777 lease = *leases.begin();
1778 }
1779
1780 // Create IA_NA that we will put in the response.
1781 // Do not use OptionDefinition to create option's instance so
1782 // as we can initialize IAID using a constructor.
1783 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
1784
1785 if (lease) {
1786 // We have a lease! Let's wrap its content into IA_NA option
1787 // with IAADDR suboption.
1788 LOG_INFO(lease6_logger, ctx.fake_allocation_ ? DHCP6_LEASE_ADVERT : DHCP6_LEASE_ALLOC)
1789 .arg(query->getLabel())
1790 .arg(lease->addr_.toText())
1791 .arg(ia->getIAID());
1793 .arg(query->getLabel())
1794 .arg(ia->getIAID())
1795 .arg(lease->toText());
1796
1797 ia_rsp->setT1(subnet->getT1());
1798 ia_rsp->setT2(subnet->getT2());
1799
1800 Option6IAAddrPtr addr(new Option6IAAddr(D6O_IAADDR, lease->addr_,
1801 lease->preferred_lft_,
1802 lease->valid_lft_));
1803 ia_rsp->addOption(addr);
1804
1805 // It would be possible to insert status code=0(success) as well,
1806 // but this is considered waste of bandwidth as absence of status
1807 // code is considered a success.
1808
1809 } else {
1810 // Allocation engine did not allocate a lease. The engine logged
1811 // cause of that failure. The only thing left is to insert
1812 // status code to pass the sad news to the client.
1813
1815 DHCP6_LEASE_ADVERT_FAIL : DHCP6_LEASE_ALLOC_FAIL)
1816 .arg(query->getLabel())
1817 .arg(ia->getIAID());
1818
1819 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
1821 "Sorry, no address could be"
1822 " allocated."));
1823 }
1824 return (ia_rsp);
1825}
1826
1828Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& /*answer*/,
1830 boost::shared_ptr<Option6IA> ia) {
1831
1832 // Check if the client sent us a hint in his IA_PD. Clients may send an
1833 // address in their IA_PD options as a suggestion (e.g. the last address
1834 // they used before). While the hint consists of a full prefix (prefix +
1835 // length), getting just the prefix is sufficient to identify a lease.
1836 Option6IAPrefixPtr hint_opt =
1837 boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
1839 if (hint_opt) {
1840 hint = hint_opt->getAddress();
1841 }
1842
1843 LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_REQUEST)
1844 .arg(query->getLabel())
1845 .arg(ia->getIAID())
1846 .arg(hint_opt ? hint.toText() : "(no hint)");
1847
1848
1849 const Subnet6Ptr& subnet = ctx.subnet_;
1850
1851 // Create IA_PD that we will put in the response.
1852 // Do not use OptionDefinition to create option's instance so
1853 // as we can initialize IAID using a constructor.
1854 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
1855
1856 // If there is no subnet selected for handling this IA_PD, the only thing
1857 // left to do is to say that we are sorry, but the user won't get an address.
1858 // As a convenience, we use a different status text to indicate that
1859 // (compare to the same status code, but different wording below)
1860 if (!subnet) {
1861
1862 // Insert status code NoAddrsAvail.
1863 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoPrefixAvail,
1864 "Sorry, no subnet available."));
1865 return (ia_rsp);
1866 }
1867
1868 // Set per-IA context values.
1869 ctx.createIAContext();
1870 ctx.currentIA().iaid_ = ia->getIAID();
1871 ctx.currentIA().addHint(hint);
1873
1874 // Use allocation engine to pick a lease for this client. Allocation engine
1875 // will try to honor the hint, but it is just a hint - some other address
1876 // may be used instead. If fake_allocation is set to false, the lease will
1877 // be inserted into the LeaseMgr as well.
1878 Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
1879
1880 if (!leases.empty()) {
1881
1882 ia_rsp->setT1(subnet->getT1());
1883 ia_rsp->setT2(subnet->getT2());
1884
1885 const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
1886
1887 for (Lease6Collection::iterator l = leases.begin();
1888 l != leases.end(); ++l) {
1889
1890 // We have a lease! Let's wrap its content into IA_PD option
1891 // with IAADDR suboption.
1893 DHCP6_PD_LEASE_ADVERT : DHCP6_PD_LEASE_ALLOC)
1894 .arg(query->getLabel())
1895 .arg((*l)->addr_.toText())
1896 .arg(static_cast<int>((*l)->prefixlen_))
1897 .arg(ia->getIAID());
1898
1899 boost::shared_ptr<Option6IAPrefix>
1900 addr(new Option6IAPrefix(D6O_IAPREFIX, (*l)->addr_,
1901 (*l)->prefixlen_, (*l)->preferred_lft_,
1902 (*l)->valid_lft_));
1903 ia_rsp->addOption(addr);
1904
1905 if (pd_exclude_requested) {
1906 // PD exclude option has been requested via ORO, thus we need to
1907 // include it if the pool configuration specifies this option.
1908 Pool6Ptr pool = boost::dynamic_pointer_cast<
1909 Pool6>(subnet->getPool(Lease::TYPE_PD, (*l)->addr_));
1910 if (pool) {
1911 Option6PDExcludePtr pd_exclude_option = pool->getPrefixExcludeOption();
1912 if (pd_exclude_option) {
1913 addr->addOption(pd_exclude_option);
1914 }
1915 }
1916 }
1917 }
1918
1919 // It would be possible to insert status code=0(success) as well,
1920 // but this is considered waste of bandwidth as absence of status
1921 // code is considered a success.
1922
1923 } else {
1924 // Allocation engine did not allocate a lease. The engine logged
1925 // cause of that failure. The only thing left is to insert
1926 // status code to pass the sad news to the client.
1927
1929 DHCP6_PD_LEASE_ADVERT_FAIL : DHCP6_PD_LEASE_ALLOC_FAIL)
1930 .arg(query->getLabel())
1931 .arg(ia->getIAID());
1932
1933 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
1935 "Sorry, no prefixes could"
1936 " be allocated."));
1937 }
1938 return (ia_rsp);
1939}
1940
1942Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
1944 boost::shared_ptr<Option6IA> ia) {
1945
1946 LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_EXTEND)
1947 .arg(query->getLabel())
1948 .arg(ia->getIAID());
1949
1950 // convenience values
1951 const Subnet6Ptr& subnet = ctx.subnet_;
1952
1953 // Create empty IA_NA option with IAID matching the request.
1954 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
1955
1956 if (!subnet) {
1966 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
1967 "Sorry, no known leases for this duid/iaid."));
1968 return (ia_rsp);
1969 }
1970
1971 // Set up T1, T2 timers
1972 ia_rsp->setT1(subnet->getT1());
1973 ia_rsp->setT2(subnet->getT2());
1974
1975 // Get DDNS update directions
1976 bool do_fwd = false;
1977 bool do_rev = false;
1978 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
1979 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
1980 if (fqdn) {
1982 do_fwd, do_rev);
1983 }
1984
1985 // Set per-packet context values.
1986 ctx.fwd_dns_update_ = do_fwd;
1987 ctx.rev_dns_update_ = do_rev;
1988
1989 // Set per-IA context values.
1990 ctx.createIAContext();
1991 ctx.currentIA().iaid_ = ia->getIAID();
1993 ctx.currentIA().ia_rsp_ = ia_rsp;
1994
1995 // Extract the addresses that the client is trying to obtain.
1996 OptionCollection addrs = ia->getOptions();
1997 for (OptionCollection::const_iterator it = addrs.begin();
1998 it != addrs.end(); ++it) {
1999 if (it->second->getType() != D6O_IAADDR) {
2000 continue;
2001 }
2002 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(it->second);
2003 if (!iaaddr) {
2004 // That's weird. Option code was ok, but the object type was not.
2005 // This should never happen. The only case would be with badly
2006 // mis-implemented hook libraries that insert invalid option objects.
2007 // There's no way to protect against this.
2008 continue;
2009 }
2010 ctx.currentIA().addHint(iaaddr->getAddress());
2011 }
2012
2013 Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
2014
2015 // Ok, now we have the leases extended. We have:
2016 // - what the client tried to renew in ctx.hints_
2017 // - what we actually assigned in leases
2018 // - old leases that are no longer valid in ctx.old_leases_
2019
2020 // For each IA inserted by the client we have to determine what to do
2021 // about included addresses and notify the client. We will iterate over
2022 // those prefixes and remove those that we have already processed. We
2023 // don't want to remove them from the context, so we need to copy them
2024 // into temporary container.
2026
2027 // For all leases we have now, add the IAADDR with non-zero lifetimes.
2028 for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
2030 (*l)->addr_, (*l)->preferred_lft_, (*l)->valid_lft_));
2031 ia_rsp->addOption(iaaddr);
2032 LOG_INFO(lease6_logger, DHCP6_LEASE_RENEW)
2033 .arg(query->getLabel())
2034 .arg((*l)->addr_.toText())
2035 .arg(ia_rsp->getIAID());
2036
2037 // Now remove this address from the hints list.
2038 AllocEngine::ResourceType hint_type((*l)->addr_, 128);
2039 hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
2040 hints.end());
2041 }
2042
2043 // For the leases that we just retired, send the addresses with 0 lifetimes.
2044 for (Lease6Collection::const_iterator l = ctx.currentIA().old_leases_.begin();
2045 l != ctx.currentIA().old_leases_.end(); ++l) {
2046
2047 // Send an address with zero lifetimes only when this lease belonged to
2048 // this client. Do not send it when we're reusing an old lease that belonged
2049 // to someone else.
2050 if (equalValues(query->getClientId(), (*l)->duid_)) {
2052 (*l)->addr_, 0, 0));
2053 ia_rsp->addOption(iaaddr);
2054 }
2055
2056 // Now remove this address from the hints list.
2057 AllocEngine::ResourceType hint_type((*l)->addr_, 128);
2058 hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
2059
2060 // If the new FQDN settings have changed for the lease, we need to
2061 // delete any existing FQDN records for this lease.
2062 if (((*l)->hostname_ != ctx.hostname_) || ((*l)->fqdn_fwd_ != do_fwd) ||
2063 ((*l)->fqdn_rev_ != do_rev)) {
2065 DHCP6_DDNS_LEASE_RENEW_FQDN_CHANGE)
2066 .arg(query->getLabel())
2067 .arg((*l)->toText())
2068 .arg(ctx.hostname_)
2069 .arg(do_rev ? "true" : "false")
2070 .arg(do_fwd ? "true" : "false");
2071
2072 queueNCR(CHG_REMOVE, *l);
2073 }
2074 }
2075
2076 // Finally, if there are any addresses requested that we haven't dealt with
2077 // already, inform the client that he can't have them.
2078 for (AllocEngine::HintContainer::const_iterator hint = hints.begin();
2079 hint != hints.end(); ++hint) {
2081 hint->first, 0, 0));
2082 ia_rsp->addOption(iaaddr);
2083 }
2084
2085 // All is left is to insert the status code.
2086 if (leases.empty()) {
2087
2088 // The server wasn't able allocate new lease and renew an existing
2089 // lease. In that case, the server sends NoAddrsAvail per RFC 8415.
2090 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2092 "Sorry, no addresses could be"
2093 " assigned at this time."));
2094 }
2095
2096 return (ia_rsp);
2097}
2098
2102 boost::shared_ptr<Option6IA> ia) {
2103
2104 LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_EXTEND)
2105 .arg(query->getLabel())
2106 .arg(ia->getIAID());
2107
2108 const Subnet6Ptr& subnet = ctx.subnet_;
2109 const DuidPtr& duid = ctx.duid_;
2110
2111 // Let's create a IA_PD response and fill it in later
2112 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
2113
2114 // If there is no subnet for the particular client, we can't retrieve
2115 // information about client's leases from lease database. We treat this
2116 // as no binding for the client.
2117 if (!subnet) {
2118 // Per RFC 8415, section 18.3.4, if there is no binding and we are
2119 // processing a Renew, the NoBinding status code should be returned.
2120 if (query->getType() == DHCPV6_RENEW) {
2121 // Insert status code NoBinding
2122 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2123 "Sorry, no known PD leases"
2124 " for this duid/iaid."));
2125 return (ia_rsp);
2126
2127 // Per RFC 8415, section 18.3.5, if there is no binding and we are
2128 // processing Rebind, the message has to be discarded (assuming that
2129 // the server doesn't know if the prefix in the IA_PD option is
2130 // appropriate for the client's link). The exception being thrown
2131 // here should propagate to the main loop and cause the message to
2132 // be discarded.
2133 } else {
2134
2143 isc_throw(DHCPv6DiscardMessageError, "no subnet found for the"
2144 " client sending Rebind to extend lifetime of the"
2145 " prefix (DUID=" << duid->toText() << ", IAID="
2146 << ia->getIAID() << ")");
2147 }
2148 }
2149
2150 // Set up T1, T2 timers
2151 ia_rsp->setT1(subnet->getT1());
2152 ia_rsp->setT2(subnet->getT2());
2153
2154 // Set per-IA context values.
2155 ctx.createIAContext();
2156 ctx.currentIA().iaid_ = ia->getIAID();
2158 ctx.currentIA().ia_rsp_ = ia_rsp;
2159
2160 // Extract prefixes that the client is trying to renew.
2161 OptionCollection addrs = ia->getOptions();
2162 for (OptionCollection::const_iterator it = addrs.begin();
2163 it != addrs.end(); ++it) {
2164 if (it->second->getType() != D6O_IAPREFIX) {
2165 continue;
2166 }
2167 Option6IAPrefixPtr prf = boost::dynamic_pointer_cast<Option6IAPrefix>(it->second);
2168 if (!prf) {
2169 // That's weird. Option code was ok, but the object type was not.
2170 // This should never happen. The only case would be with badly
2171 // mis-implemented hook libraries that insert invalid option objects.
2172 // There's no way to protect against this.
2173 continue;
2174 }
2175
2176 // Put the client's prefix into the hints list.
2177 ctx.currentIA().addHint(prf->getAddress(), prf->getLength());
2178 }
2179
2180 // Call Allocation Engine and attempt to renew leases. Number of things
2181 // may happen. Leases may be extended, revoked (if the lease is no longer
2182 // valid or reserved for someone else), or new leases may be added.
2183 // Important parameters are:
2184 // - returned container - current valid leases
2185 // - old_leases - leases that used to be, but are no longer valid
2186 // - changed_leases - leases that have FQDN changed (not really important
2187 // in PD context)
2188 Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
2189
2190 // For each IA inserted by the client we have to determine what to do
2191 // about included prefixes and notify the client. We will iterate over
2192 // those prefixes and remove those that we have already processed. We
2193 // don't want to remove them from the context, so we need to copy them
2194 // into temporary container.
2196
2197 const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
2198
2199 // For all the leases we have now, add the IAPREFIX with non-zero lifetimes
2200 for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
2201
2203 (*l)->addr_, (*l)->prefixlen_,
2204 (*l)->preferred_lft_, (*l)->valid_lft_));
2205 ia_rsp->addOption(prf);
2206
2207
2208 if (pd_exclude_requested) {
2209 // PD exclude option has been requested via ORO, thus we need to
2210 // include it if the pool configuration specifies this option.
2211 Pool6Ptr pool = boost::dynamic_pointer_cast<
2212 Pool6>(subnet->getPool(Lease::TYPE_PD, (*l)->addr_));
2213
2214 if (pool) {
2215 Option6PDExcludePtr pd_exclude_option = pool->getPrefixExcludeOption();
2216 if (pd_exclude_option) {
2217 prf->addOption(pd_exclude_option);
2218 }
2219 }
2220 }
2221
2222
2223 LOG_INFO(lease6_logger, DHCP6_PD_LEASE_RENEW)
2224 .arg(query->getLabel())
2225 .arg((*l)->addr_.toText())
2226 .arg(static_cast<int>((*l)->prefixlen_))
2227 .arg(ia->getIAID());
2228
2229 // Now remove this prefix from the hints list.
2230 AllocEngine::ResourceType hint_type((*l)->addr_, (*l)->prefixlen_);
2231 hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
2232 hints.end());
2233 }
2234
2236 for (Lease6Collection::const_iterator l = ctx.currentIA().old_leases_.begin();
2237 l != ctx.currentIA().old_leases_.end(); ++l) {
2238
2239 // Send a prefix with zero lifetimes only when this lease belonged to
2240 // this client. Do not send it when we're reusing an old lease that belonged
2241 // to someone else.
2242 if (equalValues(query->getClientId(), (*l)->duid_)) {
2243 Option6IAPrefixPtr prefix(new Option6IAPrefix(D6O_IAPREFIX, (*l)->addr_,
2244 (*l)->prefixlen_, 0, 0));
2245 ia_rsp->addOption(prefix);
2246 }
2247
2248 // Now remove this prefix from the hints list.
2249 AllocEngine::ResourceType hint_type((*l)->addr_, (*l)->prefixlen_);
2250 hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
2251 }
2252
2253 // Finally, if there are any prefixes requested that we haven't dealt with
2254 // already, inform the client that he can't have them.
2255 for (AllocEngine::HintContainer::const_iterator prefix = hints.begin();
2256 prefix != hints.end(); ++prefix) {
2257
2258 // Send the prefix with the zero lifetimes only if the prefix
2259 // contains non-zero value. A zero value indicates that the hint was
2260 // for the prefix length.
2261 if (!prefix->first.isV6Zero()) {
2262 OptionPtr prefix_opt(new Option6IAPrefix(D6O_IAPREFIX, prefix->first,
2263 prefix->second, 0, 0));
2264 ia_rsp->addOption(prefix_opt);
2265 }
2266 }
2267
2268 // All is left is to insert the status code.
2269 if (leases.empty()) {
2270
2271 // The server wasn't able allocate new lease and renew an existing
2272 // lease. In that case, the server sends NoPrefixAvail per RFC 8415.
2273 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2275 "Sorry, no prefixes could be"
2276 " assigned at this time."));
2277 }
2278
2279 return (ia_rsp);
2280}
2281
2282void
2285
2286 // We will try to extend lease lifetime for all IA options in the client's
2287 // Renew or Rebind message.
2289
2290 // For the lease extension it is critical that the client has sent
2291 // DUID. There is no need to check for the presence of the DUID here
2292 // because we have already checked it in the sanityCheck().
2293
2294 for (OptionCollection::iterator opt = query->options_.begin();
2295 opt != query->options_.end(); ++opt) {
2296 switch (opt->second->getType()) {
2297 case D6O_IA_NA: {
2298 OptionPtr answer_opt = extendIA_NA(query, reply, ctx,
2299 boost::dynamic_pointer_cast<
2300 Option6IA>(opt->second));
2301 if (answer_opt) {
2302 reply->addOption(answer_opt);
2303 }
2304 break;
2305 }
2306
2307 case D6O_IA_PD: {
2308 OptionPtr answer_opt = extendIA_PD(query, ctx,
2309 boost::dynamic_pointer_cast<
2310 Option6IA>(opt->second));
2311 if (answer_opt) {
2312 reply->addOption(answer_opt);
2313 }
2314 break;
2315 }
2316
2317 default:
2318 break;
2319 }
2320 }
2321}
2322
2323void
2326
2327 // We need to release addresses for all IA options in the client's
2328 // RELEASE message.
2329
2336
2337 // Let's set the status to be success by default. We can override it with
2338 // error status if needed. The important thing to understand here is that
2339 // the global status code may be set to success only if all IA options were
2340 // handled properly. Therefore the releaseIA_NA and releaseIA_PD options
2341 // may turn the status code to some error, but can't turn it back to success.
2342 int general_status = STATUS_Success;
2343 for (OptionCollection::iterator opt = release->options_.begin();
2344 opt != release->options_.end(); ++opt) {
2345 Lease6Ptr old_lease;
2346 switch (opt->second->getType()) {
2347 case D6O_IA_NA: {
2348 OptionPtr answer_opt = releaseIA_NA(ctx.duid_, release, general_status,
2349 boost::dynamic_pointer_cast<Option6IA>(opt->second),
2350 old_lease);
2351 if (answer_opt) {
2352 reply->addOption(answer_opt);
2353 }
2354 break;
2355 }
2356 case D6O_IA_PD: {
2357 OptionPtr answer_opt = releaseIA_PD(ctx.duid_, release, general_status,
2358 boost::dynamic_pointer_cast<Option6IA>(opt->second),
2359 old_lease);
2360 if (answer_opt) {
2361 reply->addOption(answer_opt);
2362 }
2363 break;
2364 }
2365 // @todo: add support for IA_TA
2366 default:
2367 // remaining options are stateless and thus ignored in this context
2368 ;
2369 }
2370
2371 // Store the old lease.
2372 if (old_lease) {
2373 ctx.currentIA().old_leases_.push_back(old_lease);
2374 }
2375 }
2376
2377 // Include top-level status code as well.
2378 reply->addOption(createStatusCode(*release, general_status,
2379 "Summary status for all processed IA_NAs"));
2380}
2381
2383Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
2384 int& general_status, boost::shared_ptr<Option6IA> ia,
2385 Lease6Ptr& old_lease) {
2386
2387 LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_RELEASE)
2388 .arg(query->getLabel())
2389 .arg(ia->getIAID());
2390
2391
2392 // Release can be done in one of two ways:
2393 // Approach 1: extract address from client's IA_NA and see if it belongs
2394 // to this particular client.
2395 // Approach 2: find a subnet for this client, get a lease for
2396 // this subnet/duid/iaid and check if its content matches to what the
2397 // client is asking us to release.
2398 //
2399 // This method implements approach 1.
2400
2401 // That's our response
2402 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2403
2404 Option6IAAddrPtr release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
2405 (ia->getOption(D6O_IAADDR));
2406 if (!release_addr) {
2407 ia_rsp->addOption(createStatusCode(*query, STATUS_NoBinding,
2408 "You did not include an address in your RELEASE"));
2409 general_status = STATUS_NoBinding;
2410 return (ia_rsp);
2411 }
2412
2414 release_addr->getAddress());
2415
2416 if (!lease) {
2417 // client releasing a lease that we don't know about.
2418
2419 // Insert status code NoBinding.
2420 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2421 "Sorry, no known leases for this duid/iaid, can't release."));
2422 general_status = STATUS_NoBinding;
2423
2424 return (ia_rsp);
2425 }
2426
2427 if (!lease->duid_) {
2428 // Something is gravely wrong here. We do have a lease, but it does not
2429 // have mandatory DUID information attached. Someone was messing with our
2430 // database.
2431
2432 LOG_ERROR(lease6_logger, DHCP6_LEASE_NA_WITHOUT_DUID)
2433 .arg(query->getLabel())
2434 .arg(release_addr->getAddress().toText());
2435
2436 general_status = STATUS_UnspecFail;
2437 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
2438 "Database consistency check failed when trying to RELEASE"));
2439 return (ia_rsp);
2440 }
2441
2442 if (*duid != *(lease->duid_)) {
2443
2444 // Sorry, it's not your address. You can't release it.
2445 LOG_INFO(lease6_logger, DHCP6_RELEASE_NA_FAIL_WRONG_DUID)
2446 .arg(query->getLabel())
2447 .arg(release_addr->getAddress().toText())
2448 .arg(lease->duid_->toText());
2449
2450 general_status = STATUS_NoBinding;
2451 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2452 "This address does not belong to you, you can't release it"));
2453 return (ia_rsp);
2454 }
2455
2456 if (ia->getIAID() != lease->iaid_) {
2457 // This address belongs to this client, but to a different IA
2458 LOG_WARN(lease6_logger, DHCP6_RELEASE_NA_FAIL_WRONG_IAID)
2459 .arg(query->getLabel())
2460 .arg(release_addr->getAddress().toText())
2461 .arg(lease->iaid_)
2462 .arg(ia->getIAID());
2463 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2464 "This is your address, but you used wrong IAID"));
2465 general_status = STATUS_NoBinding;
2466 return (ia_rsp);
2467 }
2468
2469 // It is not necessary to check if the address matches as we used
2470 // getLease6(addr) method that is supposed to return a proper lease.
2471
2472 bool skip = false;
2473 // Execute all callouts registered for packet6_send
2474 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
2475 CalloutHandlePtr callout_handle = getCalloutHandle(query);
2476
2477 // Use the RAII wrapper to make sure that the callout handle state is
2478 // reset when this object goes out of scope. All hook points must do
2479 // it to prevent possible circular dependency between the callout
2480 // handle and its arguments.
2481 ScopedCalloutHandleState callout_handle_state(callout_handle);
2482
2483 // Enable copying options from the packet within hook library.
2484 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
2485
2486 // Delete all previous arguments
2487 callout_handle->deleteAllArguments();
2488
2489 // Pass the original packet
2490 callout_handle->setArgument("query6", query);
2491
2492 // Pass the lease to be updated
2493 callout_handle->setArgument("lease6", lease);
2494
2495 // Call all installed callouts
2496 HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
2497
2498 // Callouts decided to skip the next processing step. The next
2499 // processing step would to send the packet, so skip at this
2500 // stage means "drop response".
2501 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
2502 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
2503 skip = true;
2504 LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP)
2505 .arg(query->getLabel());
2506 }
2507 }
2508
2509 // Ok, we've passed all checks. Let's release this address.
2510 bool success = false; // was the removal operation successful?
2511
2512 if (!skip) {
2513 success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
2514 }
2515
2516 // Here the success should be true if we removed lease successfully
2517 // and false if skip flag was set or the removal failed for whatever reason
2518
2519 if (!success) {
2520 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
2521 "Server failed to release a lease"));
2522
2523 LOG_ERROR(lease6_logger, DHCP6_RELEASE_NA_FAIL)
2524 .arg(query->getLabel())
2525 .arg(lease->addr_.toText())
2526 .arg(lease->iaid_);
2527 general_status = STATUS_UnspecFail;
2528
2529 return (ia_rsp);
2530 } else {
2531 old_lease = lease;
2532
2533 LOG_INFO(lease6_logger, DHCP6_RELEASE_NA)
2534 .arg(query->getLabel())
2535 .arg(lease->addr_.toText())
2536 .arg(lease->iaid_);
2537
2538 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
2539 "Lease released. Thank you, please come again."));
2540
2541 // Need to decrease statistic for assigned addresses.
2543 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-nas"),
2544 static_cast<int64_t>(-1));
2545
2546 // Check if a lease has flags indicating that the FQDN update has
2547 // been performed. If so, create NameChangeRequest which removes
2548 // the entries.
2549 queueNCR(CHG_REMOVE, lease);
2550
2551 return (ia_rsp);
2552 }
2553}
2554
2556Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
2557 int& general_status, boost::shared_ptr<Option6IA> ia,
2558 Lease6Ptr& old_lease) {
2559 // Release can be done in one of two ways:
2560 // Approach 1: extract address from client's IA_NA and see if it belongs
2561 // to this particular client.
2562 // Approach 2: find a subnet for this client, get a lease for
2563 // this subnet/duid/iaid and check if its content matches to what the
2564 // client is asking us to release.
2565 //
2566 // This method implements approach 1.
2567
2568 // That's our response. We will fill it in as we check the lease to be
2569 // released.
2570 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
2571
2572 boost::shared_ptr<Option6IAPrefix> release_prefix =
2573 boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
2574 if (!release_prefix) {
2575 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2576 "You did not include a prefix in your RELEASE"));
2577 general_status = STATUS_NoBinding;
2578 return (ia_rsp);
2579 }
2580
2582 release_prefix->getAddress());
2583
2584 if (!lease) {
2585 // Client releasing a lease that we don't know about.
2586
2587 // Insert status code NoBinding.
2588 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2589 "Sorry, no known leases for this duid/iaid, can't release."));
2590 general_status = STATUS_NoBinding;
2591
2592 return (ia_rsp);
2593 }
2594
2595 if (!lease->duid_) {
2596 // Something is gravely wrong here. We do have a lease, but it does not
2597 // have mandatory DUID information attached. Someone was messing with our
2598 // database.
2599 LOG_ERROR(lease6_logger, DHCP6_LEASE_PD_WITHOUT_DUID)
2600 .arg(query->getLabel())
2601 .arg(release_prefix->getAddress().toText())
2602 .arg(static_cast<int>(release_prefix->getLength()));
2603
2604 general_status = STATUS_UnspecFail;
2605 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
2606 "Database consistency check failed when trying to RELEASE"));
2607 return (ia_rsp);
2608 }
2609
2610 if (*duid != *(lease->duid_)) {
2611 // Sorry, it's not your address. You can't release it.
2612 LOG_INFO(lease6_logger, DHCP6_RELEASE_PD_FAIL_WRONG_DUID)
2613 .arg(query->getLabel())
2614 .arg(release_prefix->getAddress().toText())
2615 .arg(static_cast<int>(release_prefix->getLength()))
2616 .arg(lease->duid_->toText());
2617
2618 general_status = STATUS_NoBinding;
2619 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2620 "This address does not belong to you, you can't release it"));
2621 return (ia_rsp);
2622 }
2623
2624 if (ia->getIAID() != lease->iaid_) {
2625 // This address belongs to this client, but to a different IA
2626 LOG_WARN(lease6_logger, DHCP6_RELEASE_PD_FAIL_WRONG_IAID)
2627 .arg(query->getLabel())
2628 .arg(release_prefix->getAddress().toText())
2629 .arg(static_cast<int>(release_prefix->getLength()))
2630 .arg(lease->iaid_)
2631 .arg(ia->getIAID());
2632 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2633 "This is your address, but you used wrong IAID"));
2634 general_status = STATUS_NoBinding;
2635 return (ia_rsp);
2636 }
2637
2638 // It is not necessary to check if the address matches as we used
2639 // getLease6(addr) method that is supposed to return a proper lease.
2640
2641 bool skip = false;
2642 // Execute all callouts registered for packet6_send
2643 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
2644 CalloutHandlePtr callout_handle = getCalloutHandle(query);
2645
2646 // Use the RAII wrapper to make sure that the callout handle state is
2647 // reset when this object goes out of scope. All hook points must do
2648 // it to prevent possible circular dependency between the callout
2649 // handle and its arguments.
2650 ScopedCalloutHandleState callout_handle_state(callout_handle);
2651
2652 // Enable copying options from the packet within hook library.
2653 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
2654
2655 // Pass the original packet
2656 callout_handle->setArgument("query6", query);
2657
2658 // Pass the lease to be updated
2659 callout_handle->setArgument("lease6", lease);
2660
2661 // Call all installed callouts
2662 HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
2663
2664 skip = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2665 }
2666
2667 // Ok, we've passed all checks. Let's release this prefix.
2668 bool success = false; // was the removal operation successful?
2669
2670 if (!skip) {
2671 success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
2672 } else {
2673 // Callouts decided to skip the next processing step. The next
2674 // processing step would to send the packet, so skip at this
2675 // stage means "drop response".
2676 LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RELEASE_PD_SKIP)
2677 .arg(query->getLabel());
2678 }
2679
2680 // Here the success should be true if we removed lease successfully
2681 // and false if skip flag was set or the removal failed for whatever reason
2682
2683 if (!success) {
2684 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
2685 "Server failed to release a lease"));
2686
2687 LOG_ERROR(lease6_logger, DHCP6_RELEASE_PD_FAIL)
2688 .arg(query->getLabel())
2689 .arg(lease->addr_.toText())
2690 .arg(static_cast<int>(lease->prefixlen_))
2691 .arg(lease->iaid_);
2692 general_status = STATUS_UnspecFail;
2693
2694 } else {
2695 old_lease = lease;
2696
2697 LOG_INFO(lease6_logger, DHCP6_RELEASE_PD)
2698 .arg(query->getLabel())
2699 .arg(lease->addr_.toText())
2700 .arg(static_cast<int>(lease->prefixlen_))
2701 .arg(lease->iaid_);
2702
2703 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
2704 "Lease released. Thank you, please come again."));
2705
2706 // Need to decrease statistic for assigned prefixes.
2708 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"),
2709 static_cast<int64_t>(-1));
2710 }
2711
2712 return (ia_rsp);
2713}
2714
2715
2716Pkt6Ptr
2718
2719 Pkt6Ptr solicit = ctx.query_;
2720 Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
2721
2722 // Handle Rapid Commit option, if present.
2723 if (ctx.subnet_ && ctx.subnet_->getRapidCommit()) {
2724 OptionPtr opt_rapid_commit = solicit->getOption(D6O_RAPID_COMMIT);
2725 if (opt_rapid_commit) {
2726
2727 LOG_DEBUG(options6_logger, DBG_DHCP6_DETAIL, DHCP6_RAPID_COMMIT)
2728 .arg(solicit->getLabel());
2729
2730 // If Rapid Commit has been sent by the client, change the
2731 // response type to Reply and include Rapid Commit option.
2732 response->setType(DHCPV6_REPLY);
2733 response->addOption(opt_rapid_commit);
2734 }
2735 }
2736
2737 // "Fake" allocation is the case when the server is processing the Solicit
2738 // message without the Rapid Commit option and advertises a lease to
2739 // the client, but doesn't commit this lease to the lease database. If
2740 // the Solicit contains the Rapid Commit option and the server is
2741 // configured to honor the Rapid Commit option, or the client has sent
2742 // the Request message, the lease will be committed to the lease
2743 // database. The type of the server's response may be used to determine
2744 // if this is the fake allocation case or not. When the server sends
2745 // Reply message it means that it is committing leases. Other message
2746 // type (Advertise) means that server is not committing leases (fake
2747 // allocation).
2748 ctx.fake_allocation_ = (response->getType() != DHCPV6_REPLY);
2749
2750 processClientFqdn(solicit, response, ctx);
2751 assignLeases(solicit, response, ctx);
2752
2753 setReservedClientClasses(solicit, ctx);
2754 requiredClassify(solicit, ctx);
2755
2756 copyClientOptions(solicit, response);
2757 CfgOptionList co_list;
2758 buildCfgOptionList(solicit, ctx, co_list);
2759 appendDefaultOptions(solicit, response, co_list);
2760 appendRequestedOptions(solicit, response, co_list);
2761 appendRequestedVendorOptions(solicit, response, ctx, co_list);
2762
2763 updateReservedFqdn(ctx, response);
2764
2765 // Only generate name change requests if sending a Reply as a result
2766 // of receiving Rapid Commit option.
2767 if (response->getType() == DHCPV6_REPLY) {
2768 createNameChangeRequests(response, ctx);
2769 }
2770
2771 return (response);
2772}
2773
2774Pkt6Ptr
2776
2777 Pkt6Ptr request = ctx.query_;
2778 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
2779
2780 processClientFqdn(request, reply, ctx);
2781 assignLeases(request, reply, ctx);
2782
2783 setReservedClientClasses(request, ctx);
2784 requiredClassify(request, ctx);
2785
2786 copyClientOptions(request, reply);
2787 CfgOptionList co_list;
2788 buildCfgOptionList(request, ctx, co_list);
2789 appendDefaultOptions(request, reply, co_list);
2790 appendRequestedOptions(request, reply, co_list);
2791 appendRequestedVendorOptions(request, reply, ctx, co_list);
2792
2793 updateReservedFqdn(ctx, reply);
2794 generateFqdn(reply);
2795 createNameChangeRequests(reply, ctx);
2796
2797 return (reply);
2798}
2799
2800Pkt6Ptr
2802
2803 Pkt6Ptr renew = ctx.query_;
2804 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
2805
2806 processClientFqdn(renew, reply, ctx);
2807 extendLeases(renew, reply, ctx);
2808
2809 setReservedClientClasses(renew, ctx);
2810 requiredClassify(renew, ctx);
2811
2812 copyClientOptions(renew, reply);
2813 CfgOptionList co_list;
2814 buildCfgOptionList(renew, ctx, co_list);
2815 appendDefaultOptions(renew, reply, co_list);
2816 appendRequestedOptions(renew, reply, co_list);
2817 appendRequestedVendorOptions(renew, reply, ctx, co_list);
2818
2819 updateReservedFqdn(ctx, reply);
2820 generateFqdn(reply);
2821 createNameChangeRequests(reply, ctx);
2822
2823 return (reply);
2824}
2825
2826Pkt6Ptr
2828
2829 Pkt6Ptr rebind = ctx.query_;
2830 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
2831
2832 processClientFqdn(rebind, reply, ctx);
2833 extendLeases(rebind, reply, ctx);
2834
2835 setReservedClientClasses(rebind, ctx);
2836 requiredClassify(rebind, ctx);
2837
2838 copyClientOptions(rebind, reply);
2839 CfgOptionList co_list;
2840 buildCfgOptionList(rebind, ctx, co_list);
2841 appendDefaultOptions(rebind, reply, co_list);
2842 appendRequestedOptions(rebind, reply, co_list);
2843 appendRequestedVendorOptions(rebind, reply, ctx, co_list);
2844
2845 updateReservedFqdn(ctx, reply);
2846 generateFqdn(reply);
2847 createNameChangeRequests(reply, ctx);
2848
2849 return (reply);
2850}
2851
2852Pkt6Ptr
2854
2855 Pkt6Ptr confirm = ctx.query_;
2856 setReservedClientClasses(confirm, ctx);
2857 requiredClassify(confirm, ctx);
2858
2859 // Get IA_NAs from the Confirm. If there are none, the message is
2860 // invalid and must be discarded. There is nothing more to do.
2861 OptionCollection ias = confirm->getOptions(D6O_IA_NA);
2862 if (ias.empty()) {
2863 return (Pkt6Ptr());
2864 }
2865
2866 // The server sends Reply message in response to Confirm.
2867 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
2868 // Make sure that the necessary options are included.
2869 copyClientOptions(confirm, reply);
2870 CfgOptionList co_list;
2871 buildCfgOptionList(confirm, ctx, co_list);
2872 appendDefaultOptions(confirm, reply, co_list);
2873 appendRequestedOptions(confirm, reply, co_list);
2874 appendRequestedVendorOptions(confirm, reply, ctx, co_list);
2875 // Indicates if at least one address has been verified. If no addresses
2876 // are verified it means that the client has sent no IA_NA options
2877 // or no IAAddr options and that client's message has to be discarded.
2878 bool verified = false;
2879 // Check if subnet was selected for the message. If no subnet
2880 // has been selected, the client is not on link.
2881 SubnetPtr subnet = ctx.subnet_;
2882
2883 // Regardless if the subnet has been selected or not, we will iterate
2884 // over the IA_NA options to check if they hold any addresses. If there
2885 // are no, the Confirm is discarded.
2886 // Check addresses in IA_NA options and make sure they are appropriate.
2887 for (OptionCollection::const_iterator ia = ias.begin();
2888 ia != ias.end(); ++ia) {
2889 const OptionCollection& opts = ia->second->getOptions();
2890 for (OptionCollection::const_iterator opt = opts.begin();
2891 opt != opts.end(); ++opt) {
2892 // Ignore options other than IAAddr.
2893 if (opt->second->getType() == D6O_IAADDR) {
2894 // Check that the address is in range in the subnet selected.
2895 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
2896 Option6IAAddr>(opt->second);
2897 // If there is subnet selected and the address has been included
2898 // in IA_NA, mark it verified and verify that it belongs to the
2899 // subnet.
2900 if (iaaddr) {
2901 // If at least one address is not in range, then return
2902 // the NotOnLink status code.
2903 if (subnet && !subnet->inRange(iaaddr->getAddress())) {
2904 std::ostringstream status_msg;
2905 status_msg << "Address " << iaaddr->getAddress()
2906 << " is not on link.";
2907 reply->addOption(createStatusCode(*confirm,
2909 status_msg.str()));
2910 return (reply);
2911 }
2912 verified = true;
2913 } else {
2914 isc_throw(Unexpected, "failed to cast the IA Address option"
2915 " to the Option6IAAddrPtr. This is programming"
2916 " error and should be reported");
2917 }
2918 }
2919 }
2920 }
2921
2922 // It seems that the client hasn't included any addresses in which case
2923 // the Confirm must be discarded.
2924 if (!verified) {
2925 return (Pkt6Ptr());
2926 }
2927
2928 // If there is a subnet, there were addresses in IA_NA options and the
2929 // addresses where consistent with the subnet then the client is on link.
2930 if (subnet) {
2931 // All addresses in range, so return success.
2932 reply->addOption(createStatusCode(*confirm, STATUS_Success,
2933 "All addresses are on-link"));
2934 } else {
2935 reply->addOption(createStatusCode(*confirm, STATUS_NotOnLink,
2936 "No subnet selected"));
2937 }
2938
2939 return (reply);
2940}
2941
2942Pkt6Ptr
2944
2945 Pkt6Ptr release = ctx.query_;
2946 setReservedClientClasses(release, ctx);
2947 requiredClassify(release, ctx);
2948
2949 // Create an empty Reply message.
2950 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
2951
2952 // Copy client options (client-id, also relay information if present)
2953 copyClientOptions(release, reply);
2954
2955 // Get the configured option list
2956 CfgOptionList co_list;
2957 // buildCfgOptionList(release, ctx, co_list);
2958 appendDefaultOptions(release, reply, co_list);
2959
2960 releaseLeases(release, reply, ctx);
2961
2964
2965 return (reply);
2966}
2967
2968Pkt6Ptr
2970
2971 Pkt6Ptr decline = ctx.query_;
2972 setReservedClientClasses(decline, ctx);
2973 requiredClassify(decline, ctx);
2974
2975 // Create an empty Reply message.
2976 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
2977
2978 // Copy client options (client-id, also relay information if present)
2979 copyClientOptions(decline, reply);
2980
2981 // Get the configured option list
2982 CfgOptionList co_list;
2983 buildCfgOptionList(decline, ctx, co_list);
2984
2985 // Include server-id
2986 appendDefaultOptions(decline, reply, co_list);
2987
2988 if (declineLeases(decline, reply, ctx)) {
2989 return (reply);
2990 } else {
2991
2992 // declineLeases returns false only if the hooks set the next step
2993 // status to DROP. We'll just doing as requested.
2994 return (Pkt6Ptr());
2995 }
2996}
2997
2998bool
3001
3002 // We need to decline addresses for all IA_NA options in the client's
3003 // DECLINE message.
3004
3005 // Let's set the status to be success by default. We can override it with
3006 // error status if needed. The important thing to understand here is that
3007 // the global status code may be set to success only if all IA options were
3008 // handled properly. Therefore the declineIA options
3009 // may turn the status code to some error, but can't turn it back to success.
3010 int general_status = STATUS_Success;
3011
3012 for (OptionCollection::iterator opt = decline->options_.begin();
3013 opt != decline->options_.end(); ++opt) {
3014 switch (opt->second->getType()) {
3015 case D6O_IA_NA: {
3016 OptionPtr answer_opt = declineIA(decline, ctx.duid_, general_status,
3017 boost::dynamic_pointer_cast<Option6IA>(opt->second),
3018 ctx.new_leases_);
3019 if (answer_opt) {
3020
3021 // We have an answer, let's use it.
3022 reply->addOption(answer_opt);
3023 } else {
3024
3025 // The only case when declineIA could return NULL is if one of the
3026 // hook callouts set next step status to DROP. We just need to drop
3027 // this packet.
3028 return (false);
3029 }
3030 break;
3031 }
3032 default:
3033 // We don't care for the remaining options
3034 ;
3035 }
3036 }
3037
3038 return (true);
3039}
3040
3042Dhcpv6Srv::declineIA(const Pkt6Ptr& decline, const DuidPtr& duid,
3043 int& general_status, boost::shared_ptr<Option6IA> ia,
3044 Lease6Collection& new_leases) {
3045
3046 LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_DECLINE_PROCESS_IA)
3047 .arg(decline->getLabel())
3048 .arg(ia->getIAID());
3049
3050 // Decline can be done in one of two ways:
3051 // Approach 1: extract address from client's IA_NA and see if it belongs
3052 // to this particular client.
3053 // Approach 2: find a subnet for this client, get a lease for
3054 // this subnet/duid/iaid and check if its content matches to what the
3055 // client is asking us to decline.
3056 //
3057 // This method implements approach 1.
3058
3059 // That's our response
3060 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
3061
3062 const OptionCollection& opts = ia->getOptions();
3063 int total_addrs = 0; // Let's count the total number of addresses.
3064 for (OptionCollection::const_iterator opt = opts.begin(); opt != opts.end();
3065 ++opt) {
3066
3067 // Let's ignore nested options other than IAADDR (there shouldn't be anything
3068 // else in IA_NA in Decline message, but let's be on the safe side).
3069 if (opt->second->getType() != D6O_IAADDR) {
3070 continue;
3071 }
3072 Option6IAAddrPtr decline_addr = boost::dynamic_pointer_cast<Option6IAAddr>
3073 (opt->second);
3074 if (!decline_addr) {
3075 continue;
3076 }
3077
3078 total_addrs++;
3079
3081 decline_addr->getAddress());
3082
3083 if (!lease) {
3084 // Client trying to decline a lease that we don't know about.
3085 LOG_INFO(lease6_logger, DHCP6_DECLINE_FAIL_NO_LEASE)
3086 .arg(decline->getLabel()).arg(decline_addr->getAddress().toText());
3087
3088 // According to RFC 8415, section 18.3.8:
3089 // "For each IA in the Decline message for which the server has no
3090 // binding information, the server adds an IA option using the IAID
3091 // from the Decline message and includes a Status Code option with
3092 // the value NoBinding in the IA option".
3093 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
3094 "Server does not know about such an address."));
3095
3096 // In the same section of RFC 8415:
3097 // "The server ignores addresses not assigned to the IAs (though it may"
3098 // choose to log an error if it finds such addresses)."
3099 continue; // There may be other addresses.
3100 }
3101
3102 if (!lease->duid_) {
3103 // Something is gravely wrong here. We do have a lease, but it does not
3104 // have mandatory DUID information attached. Someone was messing with our
3105 // database.
3106
3107 LOG_ERROR(lease6_logger, DHCP6_DECLINE_FAIL_LEASE_WITHOUT_DUID)
3108 .arg(decline->getLabel())
3109 .arg(decline_addr->getAddress().toText());
3110
3111 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_UnspecFail,
3112 "Database consistency check failed when attempting Decline."));
3113
3114 continue;
3115 }
3116
3117 // Ok, there's a sane lease with an address. Let's check if DUID matches first.
3118 if (*duid != *(lease->duid_)) {
3119
3120 // Sorry, it's not your address. You can't release it.
3121 LOG_INFO(lease6_logger, DHCP6_DECLINE_FAIL_DUID_MISMATCH)
3122 .arg(decline->getLabel())
3123 .arg(decline_addr->getAddress().toText())
3124 .arg(lease->duid_->toText());
3125
3126 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
3127 "This address does not belong to you, you can't decline it"));
3128
3129 continue;
3130 }
3131
3132 // Let's check if IAID matches.
3133 if (ia->getIAID() != lease->iaid_) {
3134 // This address belongs to this client, but to a different IA
3135 LOG_INFO(lease6_logger, DHCP6_DECLINE_FAIL_IAID_MISMATCH)
3136 .arg(decline->getLabel())
3137 .arg(lease->addr_.toText())
3138 .arg(ia->getIAID())
3139 .arg(lease->iaid_);
3140 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
3141 "This is your address, but you used wrong IAID"));
3142
3143 continue;
3144 }
3145
3146 // Ok, all is good. Decline this lease.
3147 if (!declineLease(decline, lease, ia_rsp)) {
3148 // declineLease returns false only when hook callouts set the next
3149 // step status to drop. We just propagate the bad news here.
3150 return (OptionPtr());
3151
3152 } else {
3153 new_leases.push_back(lease);
3154 }
3155 }
3156
3157 if (total_addrs == 0) {
3158 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
3159 "No addresses sent in IA_NA"));
3160 general_status = STATUS_NoBinding;
3161 }
3162
3163 return (ia_rsp);
3164}
3165
3166void
3167Dhcpv6Srv::setStatusCode(boost::shared_ptr<isc::dhcp::Option6IA>& container,
3168 const OptionPtr& status) {
3169 // Let's delete any old status code we may have.
3170 container->delOption(D6O_STATUS_CODE);
3171
3172 container->addOption(status);
3173}
3174
3175bool
3176Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
3177 boost::shared_ptr<Option6IA> ia_rsp) {
3178 // We do not want to decrease the assigned-nas at this time. While
3179 // technically a declined address is no longer allocated, the primary usage
3180 // of the assigned-addresses statistic is to monitor pool utilization. Most
3181 // people would forget to include declined-addresses in the calculation,
3182 // and simply do assigned-addresses/total-addresses. This would have a bias
3183 // towards under-representing pool utilization, if we decreased allocated
3184 // immediately after receiving DHCPDECLINE, rather than later when we recover
3185 // the address.
3186
3187 // Let's call lease6_decline hooks if necessary.
3188 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_decline_)) {
3189 CalloutHandlePtr callout_handle = getCalloutHandle(decline);
3190
3191 // Use the RAII wrapper to make sure that the callout handle state is
3192 // reset when this object goes out of scope. All hook points must do
3193 // it to prevent possible circular dependency between the callout
3194 // handle and its arguments.
3195 ScopedCalloutHandleState callout_handle_state(callout_handle);
3196
3197 // Enable copying options from the packet within hook library.
3198 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(decline);
3199
3200 // Pass incoming packet as argument
3201 callout_handle->setArgument("query6", decline);
3202 callout_handle->setArgument("lease6", lease);
3203
3204 // Call callouts
3205 HooksManager::callCallouts(Hooks.hook_index_lease6_decline_,
3206 *callout_handle);
3207
3208 // Callouts decided to SKIP the next processing step. The next
3209 // processing step would to actually decline the lease, so we'll
3210 // keep the lease as is.
3211 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3212 LOG_DEBUG(hooks_logger, DBG_DHCP6_DETAIL, DHCP6_HOOK_DECLINE_SKIP)
3213 .arg(decline->getLabel())
3214 .arg(decline->getIface())
3215 .arg(lease->addr_.toText());
3216 return (true);
3217 }
3218
3219 // Callouts decided to DROP the packet. Let's simply log it and
3220 // return false, so callers will act accordingly.
3221 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
3222 LOG_DEBUG(hooks_logger, DBG_DHCP6_DETAIL, DHCP6_HOOK_DECLINE_DROP)
3223 .arg(decline->getLabel())
3224 .arg(decline->getIface())
3225 .arg(lease->addr_.toText());
3226 return (false);
3227 }
3228 }
3229
3230 // Check if a lease has flags indicating that the FQDN update has
3231 // been performed. If so, create NameChangeRequest which removes
3232 // the entries. This method does all necessary checks.
3233 queueNCR(CHG_REMOVE, lease);
3234
3235 // Bump up the subnet-specific statistic.
3237 StatsMgr::generateName("subnet", lease->subnet_id_, "declined-addresses"),
3238 static_cast<int64_t>(1));
3239
3240 // Global declined addresses counter.
3241 StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
3242
3243 // We need to disassociate the lease from the client. Once we move a lease
3244 // to declined state, it is no longer associated with the client in any
3245 // way.
3246 lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
3248
3249 LOG_INFO(lease6_logger, DHCP6_DECLINE_LEASE).arg(decline->getLabel())
3250 .arg(lease->addr_.toText()).arg(lease->valid_lft_);
3251
3252 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_Success,
3253 "Lease declined. Hopefully the next one will be better."));
3254
3255 return (true);
3256}
3257
3258Pkt6Ptr
3260
3261 Pkt6Ptr inf_request = ctx.query_;
3262 setReservedClientClasses(inf_request, ctx);
3263 requiredClassify(inf_request, ctx);
3264
3265 // Create a Reply packet, with the same trans-id as the client's.
3266 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, inf_request->getTransid()));
3267
3268 // Copy client options (client-id, also relay information if present)
3269 copyClientOptions(inf_request, reply);
3270
3271 // Build the configured option list for append methods
3272 CfgOptionList co_list;
3273 buildCfgOptionList(inf_request, ctx, co_list);
3274
3275 // Append default options, i.e. options that the server is supposed
3276 // to put in all messages it sends (server-id for now, but possibly other
3277 // options once we start supporting authentication)
3278 appendDefaultOptions(inf_request, reply, co_list);
3279
3280 // Try to assign options that were requested by the client.
3281 appendRequestedOptions(inf_request, reply, co_list);
3282
3283 // Try to assign vendor options that were requested by the client.
3284 appendRequestedVendorOptions(inf_request, reply, ctx, co_list);
3285
3286 return (reply);
3287}
3288
3289void
3291
3292 // flags are in transid
3293 // uint32_t flags = dhcp4_query->getTransid();
3294 // do nothing with DHCPV4_QUERY_FLAGS_UNICAST
3295
3296 // Get the DHCPv4 message option
3297 OptionPtr dhcp4_msg = dhcp4_query->getOption(D6O_DHCPV4_MSG);
3298 if (dhcp4_msg) {
3299 try {
3300 // Forward the whole message to the DHCPv4 server via IPC
3301 Dhcp6to4Ipc::instance().send(dhcp4_query);
3302 } catch (...) {
3303 // Assume the error was already logged
3304 return;
3305 }
3306 }
3307
3308 // This method does not return anything as we always sent back
3309 // the response via Dhcp6To4Ipc.
3310}
3311
3312void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt, std::string& classes) {
3313 OptionVendorClassPtr vclass = boost::dynamic_pointer_cast<
3314 OptionVendorClass>(pkt->getOption(D6O_VENDOR_CLASS));
3315
3316 if (!vclass || vclass->getTuplesNum() == 0) {
3317 return;
3318 }
3319
3320 if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) {
3322 classes += VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM + " ";
3323
3324 } else if (vclass->hasTuple(DOCSIS3_CLASS_EROUTER)) {
3326 classes += VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER + " ";
3327
3328 } else {
3329 pkt->addClass(VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText());
3330 classes + VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText() + " ";
3331 }
3332}
3333
3335 // All packets belongs to ALL
3336 pkt->addClass("ALL");
3337 string classes = "ALL ";
3338
3339 // First: built-in vendor class processing
3340 classifyByVendor(pkt, classes);
3341
3342 // Run match expressions on classes not depending on KNOWN/UNKNOWN.
3343 evaluateClasses(pkt, false);
3344}
3345
3346void Dhcpv6Srv::evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known) {
3347 // Note getClientClassDictionary() cannot be null
3348 const ClientClassDictionaryPtr& dict =
3349 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3350 const ClientClassDefListPtr& defs_ptr = dict->getClasses();
3351 for (ClientClassDefList::const_iterator it = defs_ptr->cbegin();
3352 it != defs_ptr->cend(); ++it) {
3353 // Note second cannot be null
3354 const ExpressionPtr& expr_ptr = (*it)->getMatchExpr();
3355 // Nothing to do without an expression to evaluate
3356 if (!expr_ptr) {
3357 continue;
3358 }
3359 // Not the right time if only when required
3360 if ((*it)->getRequired()) {
3361 continue;
3362 }
3363 // Not the right pass.
3364 if ((*it)->getDependOnKnown() != depend_on_known) {
3365 continue;
3366 }
3367 // Evaluate the expression which can return false (no match),
3368 // true (match) or raise an exception (error)
3369 try {
3370 bool status = evaluateBool(*expr_ptr, *pkt);
3371 if (status) {
3372 LOG_INFO(dhcp6_logger, EVAL_RESULT)
3373 .arg((*it)->getName())
3374 .arg(status);
3375 // Matching: add the class
3376 pkt->addClass((*it)->getName());
3377 } else {
3379 .arg((*it)->getName())
3380 .arg(status);
3381 }
3382 } catch (const Exception& ex) {
3383 LOG_ERROR(dhcp6_logger, EVAL_RESULT)
3384 .arg((*it)->getName())
3385 .arg(ex.what());
3386 } catch (...) {
3387 LOG_ERROR(dhcp6_logger, EVAL_RESULT)
3388 .arg((*it)->getName())
3389 .arg("get exception?");
3390 }
3391 }
3392}
3393
3394void
3396 const AllocEngine::ClientContext6& ctx) {
3397 if (ctx.currentHost() && pkt) {
3398 const ClientClasses& classes = ctx.currentHost()->getClientClasses6();
3399 for (ClientClasses::const_iterator cclass = classes.cbegin();
3400 cclass != classes.cend(); ++cclass) {
3401 pkt->addClass(*cclass);
3402 }
3403 }
3404
3405 const ClientClasses& classes = pkt->getClasses();
3406 if (!classes.empty()) {
3407 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED)
3408 .arg(pkt->getLabel())
3409 .arg(classes.toText());
3410 }
3411}
3412
3413void
3415 // First collect required classes
3416 ClientClasses classes = pkt->getClasses(true);
3417 Subnet6Ptr subnet = ctx.subnet_;
3418
3419 if (subnet) {
3420 // Begin by the shared-network
3421 SharedNetwork6Ptr network;
3422 subnet->getSharedNetwork(network);
3423 if (network) {
3424 const ClientClasses& to_add = network->getRequiredClasses();
3425 for (ClientClasses::const_iterator cclass = to_add.cbegin();
3426 cclass != to_add.cend(); ++cclass) {
3427 classes.insert(*cclass);
3428 }
3429 }
3430
3431 // Followed by the subnet
3432 const ClientClasses& to_add = subnet->getRequiredClasses();
3433 for (ClientClasses::const_iterator cclass = to_add.cbegin();
3434 cclass != to_add.cend(); ++cclass) {
3435 classes.insert(*cclass);
3436 }
3437
3438 // And finish by pools
3439 BOOST_FOREACH(const AllocEngine::ResourceType& resource,
3441 PoolPtr pool = ctx.subnet_->getPool(resource.second == 128 ?
3444 resource.first,
3445 false);
3446 if (pool) {
3447 const ClientClasses& to_add = pool->getRequiredClasses();
3448 for (ClientClasses::const_iterator cclass = to_add.cbegin();
3449 cclass != to_add.cend(); ++cclass) {
3450 classes.insert(*cclass);
3451 }
3452 }
3453 }
3454
3455 // host reservation???
3456 }
3457
3458 // Run match expressions
3459 // Note getClientClassDictionary() cannot be null
3460 const ClientClassDictionaryPtr& dict =
3461 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3462 for (ClientClasses::const_iterator cclass = classes.cbegin();
3463 cclass != classes.cend(); ++cclass) {
3464 const ClientClassDefPtr class_def = dict->findClass(*cclass);
3465 if (!class_def) {
3466 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_UNDEFINED)
3467 .arg(*cclass);
3468 continue;
3469 }
3470 const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
3471 // Nothing to do without an expression to evaluate
3472 if (!expr_ptr) {
3473 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_UNTESTABLE)
3474 .arg(*cclass);
3475 continue;
3476 }
3477 // Evaluate the expression which can return false (no match),
3478 // true (match) or raise an exception (error)
3479 try {
3480 bool status = evaluateBool(*expr_ptr, *pkt);
3481 if (status) {
3482 LOG_INFO(dhcp6_logger, EVAL_RESULT)
3483 .arg(*cclass)
3484 .arg(status);
3485 // Matching: add the class
3486 pkt->addClass(*cclass);
3487 } else {
3489 .arg(*cclass)
3490 .arg(status);
3491 }
3492 } catch (const Exception& ex) {
3493 LOG_ERROR(dhcp6_logger, EVAL_RESULT)
3494 .arg(*cclass)
3495 .arg(ex.what());
3496 } catch (...) {
3497 LOG_ERROR(dhcp6_logger, EVAL_RESULT)
3498 .arg(*cclass)
3499 .arg("get exception?");
3500 }
3501 }
3502}
3503
3504void
3505Dhcpv6Srv::updateReservedFqdn(const AllocEngine::ClientContext6& ctx,
3506 const Pkt6Ptr& answer) {
3507 if (!answer) {
3508 isc_throw(isc::Unexpected, "an instance of the object encapsulating"
3509 " a message must not be NULL when updating reserved FQDN");
3510 }
3511
3512 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>
3513 (answer->getOption(D6O_CLIENT_FQDN));
3514
3515 // If Client FQDN option is not included, there is nothing to do.
3516 if (!fqdn) {
3517 return;
3518 }
3519
3520 std::string name = fqdn->getDomainName();
3521
3522 // If there is a host reservation for this client we have to check whether
3523 // this reservation has the same hostname as the hostname currently
3524 // present in the FQDN option. If not, it indicates that the allocation
3525 // engine picked a different subnet (from within a shared network) for
3526 // reservations and we have to send this new value to the client.
3527 if (ctx.currentHost() &&
3528 !ctx.currentHost()->getHostname().empty()) {
3529 std::string new_name = CfgMgr::instance().getD2ClientMgr().
3530 qualifyName(ctx.currentHost()->getHostname(), true);
3531
3532 if (new_name != name) {
3533 fqdn->setDomainName(new_name, Option6ClientFqdn::FULL);
3534
3535 // Replace previous instance of Client FQDN option.
3536 answer->delOption(D6O_CLIENT_FQDN);
3537 answer->addOption(fqdn);
3538 }
3539 }
3540}
3541
3542void
3543Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer) {
3544 if (!answer) {
3545 isc_throw(isc::Unexpected, "an instance of the object encapsulating"
3546 " a message must not be NULL when generating FQDN");
3547 }
3548
3551
3552 // It is likely that client hasn't included the FQDN option. In such case,
3553 // FQDN option will be NULL. Also, there is nothing to do if the option
3554 // is present and conveys the non-empty FQDN.
3555 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
3556 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
3557 if (!fqdn || !fqdn->getDomainName().empty()) {
3558 return;
3559 }
3560
3561 // Get the first IA_NA acquired for the client.
3562 OptionPtr ia = answer->getOption(D6O_IA_NA);
3563 if (!ia) {
3564 return;
3565 }
3566
3567 // If it has any IAAddr, use the first one to generate unique FQDN.
3568 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
3569 Option6IAAddr>(ia->getOption(D6O_IAADDR));
3570 if (!iaaddr) {
3571 return;
3572 }
3573 // Get the IPv6 address acquired by the client.
3574 IOAddress addr = iaaddr->getAddress();
3575 std::string generated_name =
3577
3578 LOG_DEBUG(ddns6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_DDNS_FQDN_GENERATED)
3579 .arg(answer->getLabel())
3580 .arg(generated_name);
3581
3582 try {
3583 // The lease has been acquired but the FQDN for this lease hasn't
3584 // been updated in the lease database. We now have new FQDN
3585 // generated, so the lease database has to be updated here.
3586 // However, never update lease database for Advertise, just send
3587 // our notion of client's FQDN in the Client FQDN option.
3588 if (answer->getType() != DHCPV6_ADVERTISE) {
3589 Lease6Ptr lease =
3591 if (lease) {
3592 lease->hostname_ = generated_name;
3594
3595 } else {
3596 isc_throw(isc::Unexpected, "there is no lease in the database "
3597 " for address " << addr << ", so as it is impossible"
3598 " to update FQDN data. This is a programmatic error"
3599 " as the given address is now being handed to the"
3600 " client");
3601 }
3602 }
3603 // Set the generated FQDN in the Client FQDN option.
3604 fqdn->setDomainName(generated_name, Option6ClientFqdn::FULL);
3605
3606 answer->delOption(D6O_CLIENT_FQDN);
3607 answer->addOption(fqdn);
3608
3609 } catch (const Exception& ex) {
3610 LOG_ERROR(ddns6_logger, DHCP6_DDNS_GENERATED_FQDN_UPDATE_FAIL)
3611 .arg(answer->getLabel())
3612 .arg(addr.toText())
3613 .arg(ex.what());
3614 }
3615}
3616
3617void
3620 if (d2_mgr.ddnsEnabled()) {
3621 // Updates are enabled, so lets start the sender, passing in
3622 // our error handler.
3623 // This may throw so wherever this is called needs to ready.
3624 d2_mgr.startSender(boost::bind(&Dhcpv6Srv::d2ClientErrorHandler,
3625 this, _1, _2));
3626 }
3627}
3628
3629void
3632 if (d2_mgr.ddnsEnabled()) {
3633 // Updates are enabled, so lets stop the sender
3634 d2_mgr.stopSender();
3635 }
3636}
3637
3638void
3642 LOG_ERROR(ddns6_logger, DHCP6_DDNS_REQUEST_SEND_FAILED).
3643 arg(result).arg((ncr ? ncr->toText() : " NULL "));
3644 // We cannot communicate with kea-dhcp-ddns, suspend further updates.
3648}
3649
3650// Refer to config_report so it will be embedded in the binary
3652
3653std::string
3655 std::stringstream tmp;
3656
3657 tmp << VERSION;
3658 if (extended) {
3659 tmp << endl << EXTENDED_VERSION << endl;
3660 tmp << "linked with:" << endl;
3661 tmp << Logger::getVersion() << endl;
3662 tmp << CryptoLink::getVersion() << endl;
3663 tmp << "database:" << endl;
3664#ifdef HAVE_MYSQL
3665 tmp << MySqlLeaseMgr::getDBVersion() << endl;
3666#endif
3667#ifdef HAVE_PGSQL
3668 tmp << PgSqlLeaseMgr::getDBVersion() << endl;
3669#endif
3670#ifdef HAVE_CQL
3671 tmp << CqlLeaseMgr::getDBVersion() << endl;
3672#endif
3674
3675 // @todo: more details about database runtime
3676 }
3677
3678 return (tmp.str());
3679}
3680
3681void Dhcpv6Srv::processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp) {
3682
3683 if (query->relay_info_.empty()) {
3684 // RSOO is inserted by relay agents, nothing to do here if it's
3685 // a direct message.
3686 return;
3687 }
3688
3689 // Get RSOO configuration.
3690 ConstCfgRSOOPtr cfg_rsoo = CfgMgr::instance().getCurrentCfg()->getCfgRSOO();
3691
3692 // Let's get over all relays (encapsulation levels). We need to do
3693 // it in the same order as the client packet traversed the relays.
3694 for (int i = query->relay_info_.size(); i > 0 ; --i) {
3695 OptionPtr rsoo_container = query->getRelayOption(D6O_RSOO, i - 1);
3696 if (rsoo_container) {
3697 // There are RSOO options. Let's get through them one by one
3698 // and if it's RSOO-enabled and there's no such option provided yet,
3699 // copy it to the server's response
3700 const OptionCollection& rsoo = rsoo_container->getOptions();
3701 for (OptionCollection::const_iterator opt = rsoo.begin();
3702 opt != rsoo.end(); ++opt) {
3703
3704 // Echo option if it is RSOO enabled option and there is no such
3705 // option added yet.
3706 if (cfg_rsoo->enabled(opt->second->getType()) &&
3707 !rsp->getOption(opt->second->getType())) {
3708 rsp->addOption(opt->second);
3709 }
3710 }
3711 }
3712 }
3713}
3714
3716
3717 if (query->relay_info_.empty()) {
3718 // No relay agent
3719 return (0);
3720 }
3721
3722 // Did the last relay agent add a relay-source-port?
3723 if (query->getRelayOption(D6O_RELAY_SOURCE_PORT, 0)) {
3724 // RFC 8357 section 5.2
3725 return (query->getRemotePort());
3726 }
3727
3728 return (0);
3729}
3730
3731void Dhcpv6Srv::processStatsReceived(const Pkt6Ptr& query) {
3732 // Note that we're not bumping pkt6-received statistic as it was
3733 // increased early in the packet reception code.
3734
3735 string stat_name = "pkt6-unknown-received";
3736 switch (query->getType()) {
3737 case DHCPV6_SOLICIT:
3738 stat_name = "pkt6-solicit-received";
3739 break;
3740 case DHCPV6_ADVERTISE:
3741 // Should not happen, but let's keep a counter for it
3742 stat_name = "pkt6-advertise-received";
3743 break;
3744 case DHCPV6_REQUEST:
3745 stat_name = "pkt6-request-received";
3746 break;
3747 case DHCPV6_CONFIRM:
3748 stat_name = "pkt6-confirm-received";
3749 break;
3750 case DHCPV6_RENEW:
3751 stat_name = "pkt6-renew-received";
3752 break;
3753 case DHCPV6_REBIND:
3754 stat_name = "pkt6-rebind-received";
3755 break;
3756 case DHCPV6_REPLY:
3757 // Should not happen, but let's keep a counter for it
3758 stat_name = "pkt6-reply-received";
3759 break;
3760 case DHCPV6_RELEASE:
3761 stat_name = "pkt6-release-received";
3762 break;
3763 case DHCPV6_DECLINE:
3764 stat_name = "pkt6-decline-received";
3765 break;
3766 case DHCPV6_RECONFIGURE:
3767 stat_name = "pkt6-reconfigure-received";
3768 break;
3770 stat_name = "pkt6-infrequest-received";
3771 break;
3773 stat_name = "pkt6-dhcpv4-query-received";
3774 break;
3776 // Should not happen, but let's keep a counter for it
3777 stat_name = "pkt6-dhcpv4-response-received";
3778 break;
3779 default:
3780 ; // do nothing
3781 }
3782
3783 StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
3784}
3785
3787 // Increase generic counter for sent packets.
3788 StatsMgr::instance().addValue("pkt6-sent", static_cast<int64_t>(1));
3789
3790 // Increase packet type specific counter for packets sent.
3791 string stat_name;
3792 switch (response->getType()) {
3793 case DHCPV6_ADVERTISE:
3794 stat_name = "pkt6-advertise-sent";
3795 break;
3796 case DHCPV6_REPLY:
3797 stat_name = "pkt6-reply-sent";
3798 break;
3800 stat_name = "pkt6-dhcpv4-response-sent";
3801 break;
3802 default:
3803 // That should never happen
3804 return;
3805 }
3806
3807 StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
3808}
3809
3811 return (Hooks.hook_index_buffer6_send_);
3812}
3813
3814bool
3815Dhcpv6Srv::requestedInORO(const Pkt6Ptr& query, const uint16_t code) const {
3817 boost::dynamic_pointer_cast<OptionUint16Array>(query->getOption(D6O_ORO));
3818
3819 if (oro) {
3820 const std::vector<uint16_t>& codes = oro->getValues();
3821 return (std::find(codes.begin(), codes.end(), code) != codes.end());
3822 }
3823
3824 return (false);
3825}
3826
3828 // Dump all of our current packets, anything that is mid-stream
3829 isc::dhcp::Pkt6Ptr pkt6ptr_empty;
3830 isc::dhcp::getCalloutHandle(pkt6ptr_empty);
3832}
3833
3834};
3835};
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
DHCPv4 and DHCPv6 allocation engine.
Definition: alloc_engine.h:56
std::pair< isc::asiolink::IOAddress, uint8_t > ResourceType
Defines a single hint (an address + prefix-length).
Definition: alloc_engine.h:280
std::vector< ResourceType > HintContainer
Container for client's hints.
Definition: alloc_engine.h:283
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition: cfgmgr.cc:65
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:154
static SubnetSelector initSelector(const Pkt6Ptr &query)
Build selector from a client's message.
Definition: cfg_subnets6.cc:73
Container for storing client class names.
Definition: classify.h:43
void insert(const ClientClass &class_name)
Insert an element.
Definition: classify.h:62
std::list< ClientClass >::const_iterator const_iterator
Type of iterators.
Definition: classify.h:47
bool empty() const
Check if classes is empty.
Definition: classify.h:68
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition: classify.cc:34
const_iterator cbegin() const
Iterator to the first element.
Definition: classify.h:81
const_iterator cend() const
Iterator to the past the end element.
Definition: classify.h:86
static std::string getDBVersion()
Local version of getDBVersion() class method.
ReplaceClientNameMode
Defines the client name replacement modes.
Definition: d2_client_cfg.h:72
D2ClientMgr isolates Kea from the details of being a D2 client.
Definition: d2_client_mgr.h:79
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
const D2ClientConfigPtr & getD2ClientConfig() const
Fetches the DHCP-DDNS configuration pointer.
void startSender(D2ClientErrorHandler error_handler, isc::asiolink::IOService &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
void getUpdateDirections(const T &fqdn_resp, bool &forward, bool &reverse)
Get directional update flags based on server FQDN flags.
void suspendUpdates()
Suspends sending requests.
void adjustFqdnFlags(const T &fqdn, T &fqdn_resp)
Set server FQDN flags based on configuration and a given FQDN.
void adjustDomainName(const T &fqdn, T &fqdn_resp)
Set server FQDN name based on configuration and a given FQDN.
void sendRequest(dhcp_ddns::NameChangeRequestPtr &ncr)
Send the given NameChangeRequests to kea-dhcp-ddns.
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
std::string generateFqdn(const asiolink::IOAddress &address, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
std::string qualifyName(const std::string &partial_name, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
This exception is thrown when DHCP server hits the error which should result in discarding the messag...
Definition: dhcp6_srv.h:45
Factory for generating DUIDs (DHCP Unique Identifiers).
Definition: duid_factory.h:63
DuidPtr get()
Returns current DUID.
Holds DUID (DHCPv6 Unique Identifier)
Definition: duid.h:27
void send(const Pkt6Ptr &pkt)
Send message over IPC.
Definition: dhcp4o6_ipc.cc:226
void close()
Close communication socket.
Definition: dhcp4o6_ipc.cc:118
static Dhcp6to4Ipc & instance()
Returns pointer to the sole instance of Dhcp6to4Ipc.
Definition: dhcp6to4_ipc.cc:32
void run_one()
Main server processing step.
Definition: dhcp6_srv.cc:424
RequirementLevel
defines if certain option may, must or must not appear
Definition: dhcp6_srv.h:67
OptionPtr getServerID()
Returns server-identifier option.
Definition: dhcp6_srv.h:106
OptionPtr extendIA_PD(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Extends lifetime of the prefix.
Definition: dhcp6_srv.cc:2100
void setReservedClientClasses(const Pkt6Ptr &pkt, const AllocEngine::ClientContext6 &ctx)
Assigns classes retrieved from host reservation database.
Definition: dhcp6_srv.cc:3395
Pkt6Ptr processDecline(AllocEngine::ClientContext6 &ctx)
Process incoming Decline message.
Definition: dhcp6_srv.cc:2969
void evaluateClasses(const Pkt6Ptr &pkt, bool depend_on_known)
Evaluate classes.
Definition: dhcp6_srv.cc:3346
Pkt6Ptr processRenew(AllocEngine::ClientContext6 &ctx)
Processes incoming Renew message.
Definition: dhcp6_srv.cc:2801
static void processStatsSent(const Pkt6Ptr &response)
Updates statistics for transmitted packets.
Definition: dhcp6_srv.cc:3786
bool sanityCheck(const Pkt6Ptr &pkt)
Verifies if specified packet meets RFC requirements.
Definition: dhcp6_srv.cc:1249
static uint16_t checkRelaySourcePort(const Pkt6Ptr &query)
Used for DHCPv4-over-DHCPv6 too.
Definition: dhcp6_srv.cc:3715
void assignLeases(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Assigns leases.
Definition: dhcp6_srv.cc:1439
void stopD2()
Stops DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp6_srv.cc:3630
void copyClientOptions(const Pkt6Ptr &question, Pkt6Ptr &answer)
Copies required options from client message to server answer.
Definition: dhcp6_srv.cc:1028
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition: dhcp6_srv.h:985
virtual void sendPacket(const Pkt6Ptr &pkt)
dummy wrapper around IfaceMgr::send()
Definition: dhcp6_srv.cc:254
bool testServerID(const Pkt6Ptr &pkt)
Compare received server id with our server id.
Definition: dhcp6_srv.cc:259
void processPacketPktSend(hooks::CalloutHandlePtr &callout_handle, Pkt6Ptr &query, Pkt6Ptr &rsp)
Executes pkt6_send callout.
Definition: dhcp6_srv.cc:886
virtual void d2ClientErrorHandler(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Implements the error handler for DHCP_DDNS IO errors.
Definition: dhcp6_srv.cc:3639
OptionPtr assignIA_PD(const Pkt6Ptr &query, const isc::dhcp::Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, boost::shared_ptr< Option6IA > ia)
Processes IA_PD option (and assigns prefixes if necessary).
Definition: dhcp6_srv.cc:1828
OptionPtr declineIA(const Pkt6Ptr &decline, const DuidPtr &duid, int &general_status, boost::shared_ptr< Option6IA > ia, Lease6Collection &new_leases)
Declines leases in a single IA_NA option.
Definition: dhcp6_srv.cc:3042
virtual Pkt6Ptr receivePacket(int timeout)
dummy wrapper around IfaceMgr::receive6
Definition: dhcp6_srv.cc:250
void processPacketBufferSend(hooks::CalloutHandlePtr &callout_handle, Pkt6Ptr &rsp)
Executes buffer6_send callout and sends the response.
Definition: dhcp6_srv.cc:951
void requiredClassify(const Pkt6Ptr &pkt, AllocEngine::ClientContext6 &ctx)
Assigns incoming packet to zero or more classes (required pass).
Definition: dhcp6_srv.cc:3414
OptionPtr releaseIA_NA(const DuidPtr &duid, const Pkt6Ptr &query, int &general_status, boost::shared_ptr< Option6IA > ia, Lease6Ptr &old_lease)
Releases specific IA_NA option.
Definition: dhcp6_srv.cc:2383
void buildCfgOptionList(const Pkt6Ptr &question, AllocEngine::ClientContext6 &ctx, CfgOptionList &co_list)
Build the configured option list.
Definition: dhcp6_srv.cc:1051
void appendDefaultOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, const CfgOptionList &co_list)
Appends default options to server's answer.
Definition: dhcp6_srv.cc:1044
static const std::string VENDOR_CLASS_PREFIX
this is a prefix added to the content of vendor-class option
Definition: dhcp6_srv.h:788
OptionPtr serverid_
Server DUID (to be sent in server-identifier option)
Definition: dhcp6_srv.h:967
void initContext(const Pkt6Ptr &pkt, AllocEngine::ClientContext6 &ctx, bool &drop)
Initializes client context for specified packet.
Definition: dhcp6_srv.cc:303
Dhcpv6Srv(uint16_t port=DHCP6_SERVER_PORT)
Default constructor.
Definition: dhcp6_srv.cc:182
OptionPtr extendIA_NA(const Pkt6Ptr &query, const Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Extends lifetime of the specific IA_NA option.
Definition: dhcp6_srv.cc:1942
OptionPtr releaseIA_PD(const DuidPtr &duid, const Pkt6Ptr &query, int &general_status, boost::shared_ptr< Option6IA > ia, Lease6Ptr &old_lease)
Releases specific IA_PD option.
Definition: dhcp6_srv.cc:2556
void processDhcp4Query(const Pkt6Ptr &dhcp4_query)
Processes incoming DHCPv4-query message.
Definition: dhcp6_srv.cc:3290
Pkt6Ptr processRebind(AllocEngine::ClientContext6 &ctx)
Processes incoming Rebind message.
Definition: dhcp6_srv.cc:2827
void classifyByVendor(const Pkt6Ptr &pkt, std::string &classes)
Assign class using vendor-class-identifier option.
Definition: dhcp6_srv.cc:3312
void shutdown()
Instructs the server to shut down.
Definition: dhcp6_srv.cc:245
virtual ~Dhcpv6Srv()
Destructor. Used during DHCPv6 service shutdown.
Definition: dhcp6_srv.cc:221
Pkt6Ptr processRequest(AllocEngine::ClientContext6 &ctx)
Processes incoming Request and returns Reply response.
Definition: dhcp6_srv.cc:2775
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition: dhcp6_srv.h:993
void processPacket(Pkt6Ptr &query, Pkt6Ptr &rsp)
Process a single incoming DHCPv6 packet.
Definition: dhcp6_srv.cc:519
bool testUnicast(const Pkt6Ptr &pkt) const
Check if the message can be sent to unicast.
Definition: dhcp6_srv.cc:282
Pkt6Ptr processRelease(AllocEngine::ClientContext6 &ctx)
Process incoming Release message.
Definition: dhcp6_srv.cc:2943
void processClientFqdn(const Pkt6Ptr &question, const Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Processes Client FQDN Option.
Definition: dhcp6_srv.cc:1499
void setStatusCode(boost::shared_ptr< Option6IA > &container, const OptionPtr &status)
A simple utility method that sets the status code.
Definition: dhcp6_srv.cc:3167
static int getHookIndexBuffer6Send()
Returns the index of the buffer6_send hook.
Definition: dhcp6_srv.cc:3810
void classifyPacket(const Pkt6Ptr &pkt)
Assigns incoming packet to zero or more classes.
Definition: dhcp6_srv.cc:3334
static HWAddrPtr getMAC(const Pkt6Ptr &pkt)
Attempts to get a MAC/hardware address using configured sources.
Definition: dhcp6_srv.cc:1692
bool declineLeases(const Pkt6Ptr &decline, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to decline all leases in specified Decline message.
Definition: dhcp6_srv.cc:2999
void releaseLeases(const Pkt6Ptr &release, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to release received addresses.
Definition: dhcp6_srv.cc:2324
void extendLeases(const Pkt6Ptr &query, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to extend the lifetime of IAs.
Definition: dhcp6_srv.cc:2283
void processRSOO(const Pkt6Ptr &query, const Pkt6Ptr &rsp)
Processes Relay-supplied options, if present.
Definition: dhcp6_srv.cc:3681
static std::string getVersion(bool extended)
returns Kea version on stdout and exit.
Definition: dhcp6_srv.cc:3654
Pkt6Ptr processConfirm(AllocEngine::ClientContext6 &ctx)
Processes incoming Confirm message and returns Reply.
Definition: dhcp6_srv.cc:2853
void sanityCheckDUID(const OptionPtr &opt, const std::string &opt_name)
verifies if received DUID option (client-id or server-id) is sane
Definition: dhcp6_srv.cc:1348
bool run()
Main server processing loop.
Definition: dhcp6_srv.cc:404
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition: dhcp6_srv.h:90
void appendRequestedOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, const CfgOptionList &co_list)
Appends requested options to server's answer.
Definition: dhcp6_srv.cc:1120
volatile bool shutdown_
Indicates if shutdown is in progress.
Definition: dhcp6_srv.h:971
Pkt6Ptr processSolicit(AllocEngine::ClientContext6 &ctx)
Processes incoming Solicit and returns response.
Definition: dhcp6_srv.cc:2717
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp6_srv.cc:3618
static std::string duidToString(const OptionPtr &opt)
converts DUID to text Converts content of DUID option to a text representation, e....
Definition: dhcp6_srv.cc:1008
void createNameChangeRequests(const Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Creates a number of isc::dhcp_ddns::NameChangeRequest objects based on the DHCPv6 Client FQDN Option.
Definition: dhcp6_srv.cc:1572
Pkt6Ptr processInfRequest(AllocEngine::ClientContext6 &ctx)
Processes incoming Information-request message.
Definition: dhcp6_srv.cc:3259
isc::dhcp::Subnet6Ptr selectSubnet(const Pkt6Ptr &question, bool &drop)
Selects a subnet for a given client's packet.
Definition: dhcp6_srv.cc:1363
OptionPtr assignIA_NA(const isc::dhcp::Pkt6Ptr &query, const isc::dhcp::Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Processes IA_NA option (and assigns addresses if necessary).
Definition: dhcp6_srv.cc:1707
void appendRequestedVendorOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, const CfgOptionList &co_list)
Appends requested vendor options to server's answer.
Definition: dhcp6_srv.cc:1172
bool declineLease(const Pkt6Ptr &decline, const Lease6Ptr lease, boost::shared_ptr< Option6IA > ia_rsp)
Declines specific IPv6 lease.
Definition: dhcp6_srv.cc:3176
void discardPackets()
Discards cached and parked packets Clears the call_handle store and packet parking lots of all packet...
Definition: dhcp6_srv.cc:3827
IdentifierType
Type of the host identifier.
Definition: host.h:252
@ IDENT_HWADDR
Definition: host.h:253
@ IDENT_FLEX
Flexible host identifier.
Definition: host.h:257
std::string getIdentifierAsText() const
Returns host identifier in a textual form.
Definition: host.cc:239
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
bool send(const Pkt6Ptr &pkt)
Sends an IPv6 packet.
Definition: iface_mgr.cc:953
void closeSockets()
Closes all open sockets.
Definition: iface_mgr.cc:282
static void destroy()
Destroy lease manager.
static LeaseMgr & instance()
Return current lease manager.
virtual bool deleteLease(const isc::asiolink::IOAddress &addr)=0
Deletes a lease.
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const =0
Returns existing IPv6 lease for a given IPv6 address.
virtual void updateLease6(const Lease6Ptr &lease6)=0
Updates IPv6 lease.
static std::string getDBVersion()
Local version of getDBVersion() class method.
static std::string getDBVersion()
Local version of getDBVersion() class method.
Holds information about DHCP service enabling status.
Definition: network_state.h:57
Represents DHCPv6 Client FQDN Option (code 39).
static const uint8_t FLAG_S
S bit.
void setDomainName(const std::string &domain_name, const DomainNameType domain_name_type)
Set new domain-name.
isc::asiolink::IOAddress getAddress() const
Returns address contained within this option.
Class that represents IAPREFIX option in DHCPv6.
uint32_t getIAID() const
Returns IA identifier.
Definition: option6_ia.h:86
This class represents Status Code option (13) from RFC 8415.
Option descriptor.
Definition: cfg_option.h:35
OptionPtr option_
Option instance.
Definition: cfg_option.h:38
This class encapsulates DHCPv6 Vendor Class and DHCPv4 V-I Vendor Class options.
This class represents vendor-specific information option.
Definition: option_vendor.h:30
const OptionCollection & getOptions() const
Returns all encapsulated options.
Definition: option.h:296
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition: option.cc:201
static std::string getDBVersion()
Local version of getDBVersion() class method.
Represents a DHCPv6 packet.
Definition: pkt6.h:44
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition: pkt6.cc:616
Pool information for IPv6 addresses and prefixes.
Definition: pool.h:272
Option6PDExcludePtr getPrefixExcludeOption() const
Returns instance of the pool specific Prefix Exclude option.
Definition: pool.h:352
An exception that is thrown if a DHCPv6 protocol violation occurs while processing a message (e....
Definition: utils.h:17
RAII object enabling copying options retrieved from the packet.
Definition: pkt.h:40
Exception thrown when a call to select is interrupted by a signal.
Definition: iface_mgr.h:51
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition: option.h:52
Container class for handling the DHCID value within a NameChangeRequest.
Definition: ncr_msg.h:86
Represents a DHCP-DDNS client request.
Definition: ncr_msg.h:227
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:467
@ NEXT_STEP_PARK
park the packet
@ NEXT_STEP_CONTINUE
continue normally
@ NEXT_STEP_DROP
drop the packet
@ NEXT_STEP_SKIP
skip the next processing step
static void unloadLibraries()
Unload libraries.
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static void park(const std::string &hook_name, T parked_object, std::function< void()> unpark_callback)
Park an object (packet).
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
static HooksManager & getHooksManager()
Get singleton hooks manager.
static void clearParkingLots()
Clears any parking packets.
Wrapper class around callout handle which automatically resets handle's state.
static std::string getVersion()
Version.
Definition: log/logger.cc:49
isc::util::SignalSetPtr signal_set_
A pointer to the object installing custom signal handlers.
Definition: daemon.h:238
virtual void handleSignal()
Invokes handler for the next received signal.
Definition: daemon.cc:60
static StatsMgr & instance()
Statistics Manager accessor method.
Definition: stats_mgr.cc:21
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
Dhcp4Hooks Hooks
Definition: dhcp4_srv.cc:117
@ STATUS_NoAddrsAvail
Definition: dhcp6.h:175
@ STATUS_NoPrefixAvail
Definition: dhcp6.h:179
@ STATUS_NotOnLink
Definition: dhcp6.h:177
@ STATUS_Success
Definition: dhcp6.h:173
@ STATUS_NoBinding
Definition: dhcp6.h:176
@ STATUS_UnspecFail
Definition: dhcp6.h:174
@ D6O_CLIENT_FQDN
Definition: dhcp6.h:59
@ D6O_RSOO
Definition: dhcp6.h:86
@ D6O_SERVERID
Definition: dhcp6.h:22
@ D6O_CLIENTID
Definition: dhcp6.h:21
@ D6O_VENDOR_OPTS
Definition: dhcp6.h:37
@ D6O_RELAY_SOURCE_PORT
Definition: dhcp6.h:155
@ D6O_RAPID_COMMIT
Definition: dhcp6.h:34
@ D6O_IA_NA
Definition: dhcp6.h:23
@ D6O_ORO
Definition: dhcp6.h:26
@ D6O_PD_EXCLUDE
Definition: dhcp6.h:87
@ D6O_IA_PD
Definition: dhcp6.h:45
@ D6O_DHCPV4_MSG
Definition: dhcp6.h:107
@ D6O_IAADDR
Definition: dhcp6.h:25
@ D6O_VENDOR_CLASS
Definition: dhcp6.h:36
@ D6O_STATUS_CODE
Definition: dhcp6.h:33
@ D6O_IAPREFIX
Definition: dhcp6.h:46
@ DHCPV6_ADVERTISE
Definition: dhcp6.h:214
@ DHCPV6_REQUEST
Definition: dhcp6.h:215
@ DHCPV6_RENEW
Definition: dhcp6.h:217
@ DHCPV6_DHCPV4_QUERY
Definition: dhcp6.h:236
@ DHCPV6_DHCPV4_RESPONSE
Definition: dhcp6.h:237
@ DHCPV6_RECONFIGURE
Definition: dhcp6.h:222
@ DHCPV6_REBIND
Definition: dhcp6.h:218
@ DHCPV6_REPLY
Definition: dhcp6.h:219
@ DHCPV6_SOLICIT
Definition: dhcp6.h:213
@ DHCPV6_RELEASE
Definition: dhcp6.h:220
@ DHCPV6_INFORMATION_REQUEST
Definition: dhcp6.h:223
@ DHCPV6_CONFIRM
Definition: dhcp6.h:216
@ DHCPV6_DECLINE
Definition: dhcp6.h:221
Defines the Dhcp6to4Ipc class.
#define DOCSIS3_V6_ORO
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< OptionUint16Array > OptionUint16ArrayPtr
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
Definition: stats_mgr.cc:46
An abstract API for lease database.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
const char *const config_report[]
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:214
isc::log::Logger bad_packet6_logger(DHCP6_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition: dhcp6_log.h:93
boost::shared_ptr< Subnet > SubnetPtr
A generic pointer to either Subnet4 or Subnet6 object.
Definition: subnet.h:455
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
boost::shared_ptr< Option6PDExclude > Option6PDExcludePtr
Pointer to the Option6PDExclude object.
std::vector< uint32_t > CfgMACSources
Container for defined MAC/hardware address sources.
const int DBG_DHCP6_BASIC_DATA
Debug level used to log the traces with some basic data.
Definition: dhcp6_log.h:42
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:21
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:31
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition: lease.h:463
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition: lease.h:604
const int DBG_DHCP6_HOOKS
Debug level used to trace hook related operations.
Definition: dhcp6_log.h:33
const int DBG_DHCP6_START
Debug level used to log information during server startup.
Definition: dhcp6_log.h:21
boost::shared_ptr< Option6IA > Option6IAPtr
A pointer to the Option6IA object.
Definition: option6_ia.h:20
boost::shared_ptr< Subnet6 > Subnet6Ptr
A pointer to a Subnet6 object.
Definition: subnet.h:629
boost::shared_ptr< const CfgRSOO > ConstCfgRSOOPtr
Pointer to the const object.
Definition: cfg_rsoo.h:74
boost::shared_ptr< const CfgHostOperations > ConstCfgHostOperationsPtr
Pointer to the const object.
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:41
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
const char *const * dhcp6_config_report
Definition: dhcp6_srv.cc:3651
const char * DOCSIS3_CLASS_EROUTER
The class as specified in vendor-class option by the devices.
Definition: libdhcp++.cc:72
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
boost::shared_ptr< Option6StatusCode > Option6StatusCodePtr
Pointer to the isc::dhcp::Option6StatusCode.
std::pair< OptionContainerPersistIndex::const_iterator, OptionContainerPersistIndex::const_iterator > OptionContainerPersistRange
Pair of iterators to represent the range of options having the same persistency flag.
Definition: cfg_option.h:220
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
isc::log::Logger packet6_logger(DHCP6_PACKET_LOGGER_NAME)
Logger for processed packets.
Definition: dhcp6_log.h:99
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
boost::shared_ptr< Pool > PoolPtr
a pointer to either IPv4 or IPv6 Pool
Definition: pool.h:405
isc::hooks::CalloutHandlePtr getCalloutHandle(const T &pktptr)
CalloutHandle Store.
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
isc::log::Logger ddns6_logger(DHCP6_DDNS_LOGGER_NAME)
Logger for Hostname or FQDN processing.
Definition: dhcp6_log.h:111
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:206
boost::shared_ptr< ClientClassDefList > ClientClassDefListPtr
Defines a pointer to a ClientClassDefList.
boost::shared_ptr< Option6IAAddr > Option6IAAddrPtr
A pointer to the isc::dhcp::Option6IAAddr object.
const char * DOCSIS3_CLASS_MODEM
DOCSIS3.0 compatible cable modem.
Definition: libdhcp++.cc:69
bool evaluateBool(const Expression &expr, Pkt &pkt)
Evaluate a RPN expression for a v4 or v6 packet and return a true or false decision.
Definition: evaluate.cc:14
const int DBG_DHCP6_DETAIL_DATA
This level is used to log the contents of packets received and sent.
Definition: dhcp6_log.h:53
boost::shared_ptr< Lease6Collection > Lease6CollectionPtr
A shared pointer to the collection of IPv6 leases.
Definition: lease.h:608
OptionContainer::nth_index< 2 >::type OptionContainerPersistIndex
Type of the index #2 - option persistency flag.
Definition: cfg_option.h:215
isc::log::Logger lease6_logger(DHCP6_LEASE_LOGGER_NAME)
Logger for lease allocation logic.
Definition: dhcp6_log.h:116
boost::shared_ptr< OptionVendorClass > OptionVendorClassPtr
Defines a pointer to the OptionVendorClass.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:31
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
bool isClientClassBuiltIn(const ClientClass &client_class)
Check if a client class name is builtin.
boost::shared_ptr< Option6ClientFqdn > Option6ClientFqdnPtr
A pointer to the Option6ClientFqdn object.
const int DBG_DHCP6_BASIC
Debug level used to trace basic operations within the code.
Definition: dhcp6_log.h:30
isc::log::Logger dhcp6_logger(DHCP6_APP_LOGGER_NAME)
Base logger for DHCPv6 server.
Definition: dhcp6_log.h:87
boost::shared_ptr< Option > OptionPtr
Definition: option.h:38
isc::log::Logger options6_logger(DHCP6_OPTIONS_LOGGER_NAME)
Logger for options parser.
Definition: dhcp6_log.h:105
const int DBG_DHCP6_DETAIL
Debug level used to trace detailed errors.
Definition: dhcp6_log.h:50
std::list< ConstCfgOptionPtr > CfgOptionList
Const pointer list.
Definition: cfg_option.h:503
boost::shared_ptr< Pool6 > Pool6Ptr
a pointer an IPv6 Pool
Definition: pool.h:402
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition: hooks_log.h:37
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:65
Definition: edns.h:19
bool equalValues(const T &ptr1, const T &ptr2)
This function checks if two pointers are non-null and values are equal.
Definition: pointer_util.h:27
Defines the logger used by the top-level component of kea-dhcp-ddns.
This file provides the classes needed to embody, compose, and decompose DNS update requests that are ...
#define DHCP6_OPTION_SPACE
Definition: option_space.h:17
Lease6Collection old_leases_
A pointer to any old leases that the client had before update but are no longer valid after the updat...
Definition: alloc_engine.h:408
Option6IAPtr ia_rsp_
A pointer to the IA_NA/IA_PD option to be sent in response.
Definition: alloc_engine.h:420
Lease::Type type_
Lease type (IA or PD)
Definition: alloc_engine.h:393
Lease6Collection changed_leases_
A pointer to any leases that have changed FQDN information.
Definition: alloc_engine.h:416
uint32_t iaid_
iaid IAID field from IA_NA or IA_PD that is being processed
Definition: alloc_engine.h:390
void addHint(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128)
Convenience method adding new hint.
Context information for the DHCPv6 leases allocation.
Definition: alloc_engine.h:316
IAContext & currentIA()
Returns IA specific context for the currently processed IA.
Definition: alloc_engine.h:467
std::vector< IAContext > ias_
Container holding IA specific contexts.
Definition: alloc_engine.h:436
void addHostIdentifier(const Host::IdentifierType &id_type, const std::vector< uint8_t > &identifier)
Convenience function adding host identifier into host_identifiers_ list.
Definition: alloc_engine.h:457
bool fake_allocation_
Indicates if this is a real or fake allocation.
Definition: alloc_engine.h:331
ConstHostPtr currentHost() const
Returns host from the most preferred subnet.
DuidPtr duid_
Client identifier.
Definition: alloc_engine.h:342
Lease6Collection new_leases_
A collection of newly allocated leases.
Definition: alloc_engine.h:381
HWAddrPtr hwaddr_
Hardware/MAC address (if available, may be NULL)
Definition: alloc_engine.h:345
hooks::CalloutHandlePtr callout_handle_
Callout handle associated with the client's message.
Definition: alloc_engine.h:375
Subnet6Ptr subnet_
Subnet selected for the client by the server.
Definition: alloc_engine.h:334
ResourceContainer allocated_resources_
Holds addresses and prefixes allocated for all IAs.
Definition: alloc_engine.h:378
bool rev_dns_update_
A boolean value which indicates that server takes responsibility for the reverse DNS Update for this ...
Definition: alloc_engine.h:366
Pkt6Ptr query_
A pointer to the client's message.
Definition: alloc_engine.h:324
void createIAContext()
Creates new IA context.
Definition: alloc_engine.h:478
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
Definition: alloc_engine.h:356
bool fwd_dns_update_
A boolean value which indicates that server takes responsibility for the forward DNS Update for this ...
Definition: alloc_engine.h:361
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition: lease.h:41
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition: lease.h:39
Subnet selector used to specify parameters used to select a subnet.