Kea  2.3.5-git
dhcp6_srv.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2023 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 <dhcpsrv/fuzz.h>
17 #include <dhcp/iface_mgr.h>
18 #include <dhcp/libdhcp++.h>
19 #include <dhcp/option6_addrlst.h>
21 #include <dhcp/option6_ia.h>
22 #include <dhcp/option6_iaaddr.h>
23 #include <dhcp/option6_iaprefix.h>
25 #include <dhcp/option6_pdexclude.h>
26 #include <dhcp/option_custom.h>
27 #include <dhcp/option_vendor.h>
29 #include <dhcp/option_int_array.h>
30 #include <dhcp/pkt6.h>
31 #include <dhcp6/client_handler.h>
32 #include <dhcp6/dhcp6to4_ipc.h>
33 #include <dhcp6/dhcp6_log.h>
34 #include <dhcp6/dhcp6_srv.h>
36 #include <dhcpsrv/cfgmgr.h>
37 #include <dhcpsrv/lease_mgr.h>
39 #include <dhcpsrv/ncr_generator.h>
40 #include <dhcpsrv/subnet.h>
42 #include <dhcpsrv/utils.h>
43 #include <eval/evaluate.h>
44 #include <eval/eval_messages.h>
45 #include <exceptions/exceptions.h>
46 #include <hooks/callout_handle.h>
47 #include <hooks/hooks_log.h>
48 #include <hooks/hooks_manager.h>
49 #include <stats/stats_mgr.h>
50 #include <util/encode/hex.h>
51 #include <util/io_utilities.h>
52 #include <util/pointer_util.h>
53 #include <util/range_utilities.h>
54 #include <log/logger.h>
55 #include <cryptolink/cryptolink.h>
56 #include <cfgrpt/config_report.h>
57 
58 #ifdef HAVE_MYSQL
60 #endif
61 #ifdef HAVE_PGSQL
63 #endif
65 
66 #include <boost/foreach.hpp>
67 #include <boost/tokenizer.hpp>
68 #include <boost/algorithm/string/erase.hpp>
69 #include <boost/algorithm/string/join.hpp>
70 #include <boost/algorithm/string/split.hpp>
71 
72 #include <algorithm>
73 #include <functional>
74 #include <stdlib.h>
75 #include <time.h>
76 #include <iomanip>
77 #include <fstream>
78 #include <sstream>
79 #include <map>
80 #include <set>
81 
82 using namespace isc;
83 using namespace isc::asiolink;
84 using namespace isc::cryptolink;
85 using namespace isc::dhcp;
86 using namespace isc::dhcp_ddns;
87 using namespace isc::hooks;
88 using namespace isc::log;
89 using namespace isc::stats;
90 using namespace isc::util;
91 using namespace std;
92 namespace ph = std::placeholders;
93 
94 namespace {
95 
97 struct Dhcp6Hooks {
98  int hook_index_buffer6_receive_;
99  int hook_index_pkt6_receive_;
100  int hook_index_subnet6_select_;
101  int hook_index_leases6_committed_;
102  int hook_index_lease6_release_;
103  int hook_index_pkt6_send_;
104  int hook_index_buffer6_send_;
105  int hook_index_lease6_decline_;
106  int hook_index_host6_identifier_;
107  int hook_index_ddns6_update_;
108 
110  Dhcp6Hooks() {
111  hook_index_buffer6_receive_ = HooksManager::registerHook("buffer6_receive");
112  hook_index_pkt6_receive_ = HooksManager::registerHook("pkt6_receive");
113  hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
114  hook_index_leases6_committed_ = HooksManager::registerHook("leases6_committed");
115  hook_index_lease6_release_ = HooksManager::registerHook("lease6_release");
116  hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send");
117  hook_index_buffer6_send_ = HooksManager::registerHook("buffer6_send");
118  hook_index_lease6_decline_ = HooksManager::registerHook("lease6_decline");
119  hook_index_host6_identifier_ = HooksManager::registerHook("host6_identifier");
120  hook_index_ddns6_update_ = HooksManager::registerHook("ddns6_update");
121  }
122 };
123 
124 // Declare a Hooks object. As this is outside any function or method, it
125 // will be instantiated (and the constructor run) when the module is loaded.
126 // As a result, the hook indexes will be defined before any method in this
127 // module is called.
128 Dhcp6Hooks Hooks;
129 
141 OptionPtr
142 createStatusCode(const Pkt6& pkt, const uint16_t status_code,
143  const std::string& status_message) {
144  Option6StatusCodePtr option_status(new Option6StatusCode(status_code,
145  status_message));
147  .arg(pkt.getLabel())
148  .arg(option_status->dataToText());
149  return (option_status);
150 }
151 
166 OptionPtr
167 createStatusCode(const Pkt6& pkt, const Option6IA& ia, const uint16_t status_code,
168  const std::string& status_message) {
169  Option6StatusCodePtr option_status(new Option6StatusCode(status_code,
170  status_message));
172  .arg(pkt.getLabel())
173  .arg(ia.getIAID())
174  .arg(option_status->dataToText());
175  return (option_status);
176 }
177 
180 std::set<std::string> dhcp6_statistics = {
181  "pkt6-received",
182  "pkt6-solicit-received",
183  "pkt6-advertise-received",
184  "pkt6-request-received",
185  "pkt6-reply-received",
186  "pkt6-renew-received",
187  "pkt6-rebind-received",
188  "pkt6-decline-received",
189  "pkt6-release-received",
190  "pkt6-infrequest-received",
191  "pkt6-dhcpv4-query-received",
192  "pkt6-dhcpv4-response-received",
193  "pkt6-unknown-received",
194  "pkt6-sent",
195  "pkt6-advertise-sent",
196  "pkt6-reply-sent",
197  "pkt6-dhcpv4-response-sent",
198  "pkt6-parse-failed",
199  "pkt6-receive-drop",
200  "v6-allocation-fail",
201  "v6-allocation-fail-shared-network",
202  "v6-allocation-fail-subnet",
203  "v6-allocation-fail-no-pools",
204  "v6-allocation-fail-classes"
205 };
206 
207 } // namespace
208 
209 namespace isc {
210 namespace dhcp {
211 
212 const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
213 
214 Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port)
215  : io_service_(new IOService()), server_port_(server_port),
216  client_port_(client_port), serverid_(), shutdown_(true),
217  alloc_engine_(), name_change_reqs_(),
218  network_state_(new NetworkState(NetworkState::DHCPv6)),
219  cb_control_(new CBControlDHCPv6()) {
221  .arg(server_port);
222 
223  Dhcp6to4Ipc::instance().client_port = client_port;
224 
225  // Initialize objects required for DHCP server operation.
226  try {
227  // Port 0 is used for testing purposes where in most cases we don't
228  // rely on the physical interfaces. Therefore, it should be possible
229  // to create an object even when there are no usable interfaces.
230  if ((server_port > 0) && (IfaceMgr::instance().countIfaces() == 0)) {
232  return;
233  }
234 
235  // Create a DUID instance but do not store it into a file.
236  DUIDFactory duid_factory;
237  DuidPtr duid = duid_factory.get();
238  serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
239 
240  // Instantiate allocation engine. The number of allocation attempts equal
241  // to zero indicates that the allocation engine will use the number of
242  // attempts depending on the pool size.
243  alloc_engine_.reset(new AllocEngine(0));
244 
246 
247  } catch (const std::exception &e) {
249  return;
250  }
251  // Initializing all observations with default value
253 
254  // All done, so can proceed
255  shutdown_ = false;
256 }
257 
260 
261  // Iterate over set of observed statistics
262  for (auto it = dhcp6_statistics.begin(); it != dhcp6_statistics.end(); ++it) {
263  // Initialize them with default value 0
264  stats_mgr.setValue((*it), static_cast<int64_t>(0));
265  }
266 }
267 
269  // Discard any parked packets
270  discardPackets();
271 
272  try {
273  stopD2();
274  } catch (const std::exception& ex) {
275  // Highly unlikely, but lets Report it but go on
277  }
278 
279  try {
281  } catch (const std::exception& ex) {
282  // Highly unlikely, but lets Report it but go on
283  // LOG_ERROR(dhcp6_logger, DHCP6_SRV_DHCP4O6_ERROR).arg(ex.what());
284  }
285 
287 
289 
290  // Explicitly unload hooks
291  HooksManager::prepareUnloadLibraries();
292  if (!HooksManager::unloadLibraries()) {
293  auto names = HooksManager::getLibraryNames();
294  std::string msg;
295  if (!names.empty()) {
296  msg = names[0];
297  for (size_t i = 1; i < names.size(); ++i) {
298  msg += std::string(", ") + names[i];
299  }
300  }
302  }
303 }
304 
307  shutdown_ = true;
308 }
309 
311  return (IfaceMgr::instance().receive6(timeout));
312 }
313 
314 void Dhcpv6Srv::sendPacket(const Pkt6Ptr& packet) {
315  IfaceMgr::instance().send(packet);
316 }
317 
318 bool
324  OptionPtr server_id = pkt->getOption(D6O_SERVERID);
325  if (server_id){
326  // Let us test received ServerID if it is same as ServerID
327  // which is being used by server
328  if (getServerID()->getData() != server_id->getData()){
330  .arg(pkt->getLabel())
331  .arg(duidToString(server_id))
332  .arg(duidToString(getServerID()));
333  return (false);
334  }
335  }
336  // return True if: no serverid received or ServerIDs matching
337  return (true);
338 }
339 
340 bool
341 Dhcpv6Srv::testUnicast(const Pkt6Ptr& pkt) const {
342  switch (pkt->getType()) {
343  case DHCPV6_SOLICIT:
344  case DHCPV6_CONFIRM:
345  case DHCPV6_REBIND:
347  if (pkt->relay_info_.empty() && !pkt->getLocalAddr().isV6Multicast()) {
349  .arg(pkt->getLabel())
350  .arg(pkt->getName());
351  return (false);
352  }
353  break;
354  default:
355  // do nothing
356  ;
357  }
358  return (true);
359 }
360 
361 void
363  const ConstCfgHostOperationsPtr cfg =
364  CfgMgr::instance().getCurrentCfg()->getCfgHostOperations6();
365  BOOST_FOREACH(const Host::IdentifierType& id_type,
366  cfg->getIdentifierTypes()) {
367  switch (id_type) {
368  case Host::IDENT_DUID:
369  if (ctx.duid_) {
370  ctx.addHostIdentifier(id_type, ctx.duid_->getDuid());
371  }
372  break;
373 
374  case Host::IDENT_HWADDR:
375  if (ctx.hwaddr_) {
376  ctx.addHostIdentifier(id_type, ctx.hwaddr_->hwaddr_);
377  }
378  break;
379  case Host::IDENT_FLEX:
380  // At this point the information in the packet has been unpacked into
381  // the various packet fields and option objects has been created.
382  // Execute callouts registered for host6_identifier.
383  if (HooksManager::calloutsPresent(Hooks.hook_index_host6_identifier_)) {
384  CalloutHandlePtr callout_handle = getCalloutHandle(ctx.query_);
385 
387  std::vector<uint8_t> id;
388 
389  // Use the RAII wrapper to make sure that the callout handle state is
390  // reset when this object goes out of scope. All hook points must do
391  // it to prevent possible circular dependency between the callout
392  // handle and its arguments.
393  ScopedCalloutHandleState callout_handle_state(callout_handle);
394 
395  // Pass incoming packet as argument
396  callout_handle->setArgument("query6", ctx.query_);
397  callout_handle->setArgument("id_type", type);
398  callout_handle->setArgument("id_value", id);
399 
400  // Call callouts
401  HooksManager::callCallouts(Hooks.hook_index_host6_identifier_,
402  *callout_handle);
403 
404  callout_handle->getArgument("id_type", type);
405  callout_handle->getArgument("id_value", id);
406 
407  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_CONTINUE) &&
408  !id.empty()) {
409 
411  .arg(Host::getIdentifierAsText(type, &id[0], id.size()));
412 
413  ctx.addHostIdentifier(type, id);
414  }
415  }
416  break;
417  default:
418  ;
419  }
420  }
421 }
422 
423 bool
426  // Pointer to client's query.
427  ctx.query_ = query;
428 
429  // DUID.
430  ctx.duid_ = query->getClientId();
431 
432  // Hardware address.
433  ctx.hwaddr_ = getMAC(query);
434 
435  // Get the early-global-reservations-lookup flag value.
438  if (egrl) {
439  ctx.early_global_reservations_lookup_ = egrl->boolValue();
440  }
441 
442  // Perform early global reservations lookup when wanted.
444  // Get the host identifiers.
445  setHostIdentifiers(ctx);
446 
447  // Check for global host reservations.
448  ConstHostPtr global_host = alloc_engine_->findGlobalReservation(ctx);
449 
450  if (global_host && !global_host->getClientClasses6().empty()) {
451  // Remove dependent evaluated classes.
453 
454  // Add classes from the global reservations.
455  const ClientClasses& classes = global_host->getClientClasses6();
456  for (ClientClasses::const_iterator cclass = classes.cbegin();
457  cclass != classes.cend(); ++cclass) {
458  query->addClass(*cclass);
459  }
460 
461  // Evaluate classes before KNOWN.
462  evaluateClasses(query, false);
463  }
464 
465  if (global_host) {
466  // Add the KNOWN class;
467  query->addClass("KNOWN");
469  .arg(query->getLabel())
470  .arg("KNOWN");
471 
472  // Evaluate classes after KNOWN.
473  evaluateClasses(query, true);
474 
475  // Check the DROP special class.
476  if (query->inClass("DROP")) {
479  .arg(query->toText());
480  StatsMgr::instance().addValue("pkt6-receive-drop",
481  static_cast<int64_t>(1));
482  return (false);
483  }
484 
485  // Store the reservation.
486  ctx.hosts_[SUBNET_ID_GLOBAL] = global_host;
487  }
488  }
489 
490  return (true);
491 }
492 
493 void
496  bool& drop) {
497  ctx.subnet_ = selectSubnet(pkt, drop);
498  ctx.fwd_dns_update_ = false;
499  ctx.rev_dns_update_ = false;
500  ctx.hostname_ = "";
502 
503  if (drop) {
504  // Caller will immediately drop the packet so simply return now.
505  return;
506  }
507 
508  // Collect host identifiers if host reservations enabled. The identifiers
509  // are stored in order of preference. The server will use them in that
510  // order to search for host reservations.
512  if (ctx.subnet_) {
513  // Before we can check for static reservations, we need to prepare
514  // a set of identifiers to be used for this.
516  setHostIdentifiers(ctx);
517  }
518 
519  // Find host reservations using specified identifiers.
520  alloc_engine_->findReservation(ctx);
521 
522  // Get shared network to see if it is set for a subnet.
523  ctx.subnet_->getSharedNetwork(sn);
524  }
525 
526  // Global host reservations are independent of a selected subnet. If the
527  // global reservations contain client classes we should use them in case
528  // they are meant to affect pool selection. Also, if the subnet does not
529  // belong to a shared network we can use the reserved client classes
530  // because there is no way our subnet could change. Such classes may
531  // affect selection of a pool within the selected subnet.
532  auto global_host = ctx.globalHost();
533  auto current_host = ctx.currentHost();
535  global_host && !global_host->getClientClasses6().empty()) ||
536  (!sn && current_host && !current_host->getClientClasses6().empty())) {
537  // We have already evaluated client classes and some of them may
538  // be in conflict with the reserved classes. Suppose there are
539  // two classes defined in the server configuration: first_class
540  // and second_class and the test for the second_class it looks
541  // like this: "not member('first_class')". If the first_class
542  // initially evaluates to false, the second_class evaluates to
543  // true. If the first_class is now set within the hosts reservations
544  // and we don't remove the previously evaluated second_class we'd
545  // end up with both first_class and second_class evaluated to
546  // true. In order to avoid that, we have to remove the classes
547  // evaluated in the first pass and evaluate them again. As
548  // a result, the first_class set via the host reservation will
549  // replace the second_class because the second_class will this
550  // time evaluate to false as desired.
552  setReservedClientClasses(pkt, ctx);
553  evaluateClasses(pkt, false);
554  }
555 
556  // Set KNOWN builtin class if something was found, UNKNOWN if not.
557  if (!ctx.hosts_.empty()) {
558  pkt->addClass("KNOWN");
560  .arg(pkt->getLabel())
561  .arg("KNOWN");
562  } else {
563  pkt->addClass("UNKNOWN");
565  .arg(pkt->getLabel())
566  .arg("UNKNOWN");
567  }
568 
569  // Perform second pass of classification.
570  evaluateClasses(pkt, true);
571 
572  const ClientClasses& classes = pkt->getClasses();
573  if (!classes.empty()) {
575  .arg(pkt->getLabel())
576  .arg(classes.toText());
577  }
578 
579  // Check the DROP special class.
580  if (pkt->inClass("DROP")) {
582  .arg(pkt->toText());
583  StatsMgr::instance().addValue("pkt6-receive-drop",
584  static_cast<int64_t>(1));
585  drop = true;
586  }
587 }
588 
590 #ifdef ENABLE_AFL
591  // Set up structures needed for fuzzing.
592  Fuzz fuzzer(6, server_port_);
593  //
594  // The next line is needed as a signature for AFL to recognize that we are
595  // running persistent fuzzing. This has to be in the main image file.
596  while (__AFL_LOOP(fuzzer.maxLoopCount())) {
597  // Read from stdin and put the data read into an address/port on which
598  // Kea is listening, read for Kea to read it via asynchronous I/O.
599  fuzzer.transfer();
600 #else
601  while (!shutdown_) {
602 #endif // ENABLE_AFL
603  try {
604  run_one();
605  getIOService()->poll();
606  } catch (const std::exception& e) {
607  // General catch-all standard exceptions that are not caught by more
608  // specific catches.
610  .arg(e.what());
611 
612  } catch (...) {
613  // General catch-all non-standard exception that are not caught
614  // by more specific catches.
616  }
617  }
618 
619  // Stop everything before we change into single-threaded mode.
621 
622  // destroying the thread pool
623  MultiThreadingMgr::instance().apply(false, 0, 0);
624 
625  return (getExitValue());
626 }
627 
629  // client's message and server's response
630  Pkt6Ptr query;
631 
632  try {
633  // Set select() timeout to 1s. This value should not be modified
634  // because it is important that the select() returns control
635  // frequently so as the IOService can be polled for ready handlers.
636  uint32_t timeout = 1;
637  query = receivePacket(timeout);
638 
639  // Log if packet has arrived. We can't log the detailed information
640  // about the DHCP message because it hasn't been unpacked/parsed
641  // yet, and it can't be parsed at this point because hooks will
642  // have to process it first. The only information available at this
643  // point are: the interface, source address and destination addresses
644  // and ports.
645  if (query) {
647  .arg(query->getRemoteAddr().toText())
648  .arg(query->getRemotePort())
649  .arg(query->getLocalAddr().toText())
650  .arg(query->getLocalPort())
651  .arg(query->getIface());
652 
653  // Log reception of the packet. We need to increase it early, as
654  // any failures in unpacking will cause the packet to be dropped.
655  // we will increase type specific packets further down the road.
656  // See processStatsReceived().
657  StatsMgr::instance().addValue("pkt6-received", static_cast<int64_t>(1));
658  }
659 
660  // We used to log that the wait was interrupted, but this is no longer
661  // the case. Our wait time is 1s now, so the lack of query packet more
662  // likely means that nothing new appeared within a second, rather than
663  // we were interrupted. And we don't want to print a message every
664  // second.
665 
666  } catch (const SignalInterruptOnSelect&) {
667  // Packet reception interrupted because a signal has been received.
668  // This is not an error because we might have received a SIGTERM,
669  // SIGINT, SIGHUP or SIGCHLD which are handled by the server. For
670  // signals that are not handled by the server we rely on the default
671  // behavior of the system.
673  } catch (const std::exception& e) {
675  }
676 
677  // Timeout may be reached or signal received, which breaks select()
678  // with no packet received
679  if (!query) {
680  return;
681  }
682 
683  // If the DHCP service has been globally disabled, drop the packet.
684  if (!network_state_->isServiceEnabled()) {
686  .arg(query->getLabel());
687  return;
688  } else {
689  if (MultiThreadingMgr::instance().getMode()) {
690  typedef function<void()> CallBack;
691  boost::shared_ptr<CallBack> call_back =
692  boost::make_shared<CallBack>(std::bind(&Dhcpv6Srv::processPacketAndSendResponseNoThrow,
693  this, query));
694  if (!MultiThreadingMgr::instance().getThreadPool().add(call_back)) {
696  }
697  } else {
699  }
700  }
701 }
702 
703 void
705  try {
707  } catch (const std::exception& e) {
709  .arg(e.what());
710  } catch (...) {
712  }
713 }
714 
715 void
717  Pkt6Ptr rsp;
718  processPacket(query, rsp);
719  if (!rsp) {
720  return;
721  }
722 
723  CalloutHandlePtr callout_handle = getCalloutHandle(query);
724  processPacketBufferSend(callout_handle, rsp);
725 }
726 
727 void
729  // All packets belong to ALL.
730  query->addClass("ALL");
731 
732  bool skip_unpack = false;
733 
734  // The packet has just been received so contains the uninterpreted wire
735  // data; execute callouts registered for buffer6_receive.
736  if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
737  CalloutHandlePtr callout_handle = getCalloutHandle(query);
738 
739  // Use the RAII wrapper to make sure that the callout handle state is
740  // reset when this object goes out of scope. All hook points must do
741  // it to prevent possible circular dependency between the callout
742  // handle and its arguments.
743  ScopedCalloutHandleState callout_handle_state(callout_handle);
744 
745  // Enable copying options from the packet within hook library.
746  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
747 
748  // Pass incoming packet as argument
749  callout_handle->setArgument("query6", query);
750 
751  // Call callouts
752  HooksManager::callCallouts(Hooks.hook_index_buffer6_receive_, *callout_handle);
753 
754  // Callouts decided to skip the next processing step. The next
755  // processing step would be to parse the packet, so skip at this
756  // stage means that callouts did the parsing already, so server
757  // should skip parsing.
758  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
760  .arg(query->getRemoteAddr().toText())
761  .arg(query->getLocalAddr().toText())
762  .arg(query->getIface());
763  skip_unpack = true;
764  }
765 
766  // Callouts decided to drop the received packet
767  // The response (rsp) is null so the caller (run_one) will
768  // immediately return too.
769  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
771  .arg(query->getRemoteAddr().toText())
772  .arg(query->getLocalAddr().toText())
773  .arg(query->getIface());
774 
775  // Increase the statistic of dropped packets.
776  StatsMgr::instance().addValue("pkt6-receive-drop",
777  static_cast<int64_t>(1));
778  return;
779  }
780 
781  callout_handle->getArgument("query6", query);
782  }
783 
784  // Unpack the packet information unless the buffer6_receive callouts
785  // indicated they did it
786  if (!skip_unpack) {
787  try {
789  .arg(query->getRemoteAddr().toText())
790  .arg(query->getLocalAddr().toText())
791  .arg(query->getIface());
792  query->unpack();
793  } catch (const SkipRemainingOptionsError& e) {
794  // An option failed to unpack but we are to attempt to process it
795  // anyway. Log it and let's hope for the best.
798  .arg(e.what());
799  } catch (const std::exception &e) {
800  // Failed to parse the packet.
802  .arg(query->getRemoteAddr().toText())
803  .arg(query->getLocalAddr().toText())
804  .arg(query->getIface())
805  .arg(e.what());
806 
807  // Increase the statistics of parse failures and dropped packets.
808  StatsMgr::instance().addValue("pkt6-parse-failed",
809  static_cast<int64_t>(1));
810  StatsMgr::instance().addValue("pkt6-receive-drop",
811  static_cast<int64_t>(1));
812  return;
813  }
814  }
815 
816  // Update statistics accordingly for received packet.
817  processStatsReceived(query);
818 
819  // Check if received query carries server identifier matching
820  // server identifier being used by the server.
821  if (!testServerID(query)) {
822 
823  // Increase the statistic of dropped packets.
824  StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
825  return;
826  }
827 
828  // Check if the received query has been sent to unicast or multicast.
829  // The Solicit, Confirm, Rebind and Information Request will be
830  // discarded if sent to unicast address.
831  if (!testUnicast(query)) {
832 
833  // Increase the statistic of dropped packets.
834  StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
835  return;
836  }
837 
838  // Assign this packet to a class, if possible
839  classifyPacket(query);
840 
842  .arg(query->getLabel())
843  .arg(query->getName())
844  .arg(static_cast<int>(query->getType()))
845  .arg(query->getRemoteAddr())
846  .arg(query->getLocalAddr())
847  .arg(query->getIface());
849  .arg(query->getLabel())
850  .arg(query->toText());
851 
852  // At this point the information in the packet has been unpacked into
853  // the various packet fields and option objects has been created.
854  // Execute callouts registered for packet6_receive.
855  if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
856  CalloutHandlePtr callout_handle = getCalloutHandle(query);
857 
858  // Use the RAII wrapper to make sure that the callout handle state is
859  // reset when this object goes out of scope. All hook points must do
860  // it to prevent possible circular dependency between the callout
861  // handle and its arguments.
862  ScopedCalloutHandleState callout_handle_state(callout_handle);
863 
864  // Enable copying options from the packet within hook library.
865  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
866 
867  // Pass incoming packet as argument
868  callout_handle->setArgument("query6", query);
869 
870  // Call callouts
871  HooksManager::callCallouts(Hooks.hook_index_pkt6_receive_, *callout_handle);
872 
873  // Callouts decided to skip the next processing step. The next
874  // processing step would be to process the packet, so skip at this
875  // stage means drop.
876  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
877  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
879  .arg(query->getLabel());
880  // Increase the statistic of dropped packets.
881  StatsMgr::instance().addValue("pkt6-receive-drop",
882  static_cast<int64_t>(1));
883  return;
884  }
885 
886  callout_handle->getArgument("query6", query);
887  }
888 
889  // Reject the message if it doesn't pass the sanity check.
890  if (!sanityCheck(query)) {
891  return;
892  }
893 
894  // Check the DROP special class.
895  if (query->inClass("DROP")) {
897  .arg(query->toText());
898  StatsMgr::instance().addValue("pkt6-receive-drop",
899  static_cast<int64_t>(1));
900  return;
901  }
902 
903  processDhcp6Query(query, rsp);
904 }
905 
906 void
908  try {
909  processDhcp6Query(query, rsp);
910  if (!rsp) {
911  return;
912  }
913 
914  CalloutHandlePtr callout_handle = getCalloutHandle(query);
915  processPacketBufferSend(callout_handle, rsp);
916  } catch (const std::exception& e) {
918  .arg(e.what());
919  } catch (...) {
921  }
922 }
923 
924 void
926  // Create a client race avoidance RAII handler.
927  ClientHandler client_handler;
928 
929  // Check for lease modifier queries from the same client being processed.
930  if (MultiThreadingMgr::instance().getMode() &&
931  ((query->getType() == DHCPV6_SOLICIT) ||
932  (query->getType() == DHCPV6_REQUEST) ||
933  (query->getType() == DHCPV6_RENEW) ||
934  (query->getType() == DHCPV6_REBIND) ||
935  (query->getType() == DHCPV6_RELEASE) ||
936  (query->getType() == DHCPV6_DECLINE))) {
937  ContinuationPtr cont =
939  this, query, rsp));
940  if (!client_handler.tryLock(query, cont)) {
941  return;
942  }
943  }
944 
945  // Let's create a simplified client context here.
947  if (!earlyGHRLookup(query, ctx)) {
948  return;
949  }
950 
951  if (query->getType() == DHCPV6_DHCPV4_QUERY) {
952  // This call never throws. Should this change, this section must be
953  // enclosed in try-catch.
954  processDhcp4Query(query);
955  return;
956  }
957 
958  // Complete the client context initialization.
959  bool drop = false;
960  initContext(query, ctx, drop);
961 
962  // Stop here if initContext decided to drop the packet.
963  if (drop) {
964  return;
965  }
966 
967  // Park point here.
968 
969  try {
970  switch (query->getType()) {
971  case DHCPV6_SOLICIT:
972  rsp = processSolicit(ctx);
973  break;
974 
975  case DHCPV6_REQUEST:
976  rsp = processRequest(ctx);
977  break;
978 
979  case DHCPV6_RENEW:
980  rsp = processRenew(ctx);
981  break;
982 
983  case DHCPV6_REBIND:
984  rsp = processRebind(ctx);
985  break;
986 
987  case DHCPV6_CONFIRM:
988  rsp = processConfirm(ctx);
989  break;
990 
991  case DHCPV6_RELEASE:
992  rsp = processRelease(ctx);
993  break;
994 
995  case DHCPV6_DECLINE:
996  rsp = processDecline(ctx);
997  break;
998 
1000  rsp = processInfRequest(ctx);
1001  break;
1002 
1003  default:
1004  return;
1005  }
1006 
1007  } catch (const std::exception& e) {
1008 
1009  // Catch-all exception (at least for ones based on the isc Exception
1010  // class, which covers more or less all that are explicitly raised
1011  // in the Kea code), but also the standard one, which may possibly be
1012  // thrown from boost code. Just log the problem and ignore the packet.
1013  // (The problem is logged as a debug message because debug is
1014  // disabled by default - it prevents a DDOS attack based on the
1015  // sending of problem packets.)
1017  .arg(query->getName())
1018  .arg(query->getRemoteAddr().toText())
1019  .arg(e.what());
1020 
1021  // Increase the statistic of dropped packets.
1022  StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
1023  }
1024 
1025  if (!rsp) {
1026  return;
1027  }
1028 
1029  // Process relay-supplied options. It is important to call this very
1030  // late in the process, because we now have all the options the
1031  // server wanted to send already set. This is important, because
1032  // RFC6422, section 6 states:
1033  //
1034  // The server SHOULD discard any options that appear in the RSOO
1035  // for which it already has one or more candidates.
1036  //
1037  // So we ignore any RSOO options if there's an option with the same
1038  // code already present.
1039  processRSOO(query, rsp);
1040 
1041  rsp->setRemoteAddr(query->getRemoteAddr());
1042  rsp->setLocalAddr(query->getLocalAddr());
1043 
1044  if (client_port_) {
1045  // A command line option enforces a specific client port
1046  rsp->setRemotePort(client_port_);
1047  } else if (rsp->relay_info_.empty()) {
1048  // Direct traffic, send back to the client directly
1049  rsp->setRemotePort(DHCP6_CLIENT_PORT);
1050  } else {
1051  // Relayed traffic, send back to the relay agent
1052  uint16_t relay_port = checkRelaySourcePort(query);
1053  rsp->setRemotePort(relay_port ? relay_port : DHCP6_SERVER_PORT);
1054  }
1055 
1056  if (server_port_) {
1057  rsp->setLocalPort(server_port_);
1058  } else {
1059  rsp->setLocalPort(DHCP6_SERVER_PORT);
1060  }
1061  rsp->setIndex(query->getIndex());
1062  rsp->setIface(query->getIface());
1063 
1064  CalloutHandlePtr callout_handle = getCalloutHandle(query);
1065  if (!ctx.fake_allocation_ && (ctx.query_->getType() != DHCPV6_CONFIRM) &&
1066  (ctx.query_->getType() != DHCPV6_INFORMATION_REQUEST) &&
1067  HooksManager::calloutsPresent(Hooks.hook_index_leases6_committed_)) {
1068  // The ScopedCalloutHandleState class which guarantees that the task
1069  // is added to the thread pool after the response is reset (if needed)
1070  // and CalloutHandle state is reset. In ST it does nothing.
1071  // A smart pointer is used to store the ScopedCalloutHandleState so that
1072  // a copy of the pointer is created by the lambda and only on the
1073  // destruction of the last reference the task is added.
1074  // In MT there are 2 cases:
1075  // 1. packet is unparked before current thread smart pointer to
1076  // ScopedCalloutHandleState is destroyed:
1077  // - the lambda uses the smart pointer to set the callout which adds the
1078  // task, but the task is added after ScopedCalloutHandleState is
1079  // destroyed, on the destruction of the last reference which is held
1080  // by the current thread.
1081  // 2. packet is unparked after the current thread smart pointer to
1082  // ScopedCalloutHandleState is destroyed:
1083  // - the current thread reference to ScopedCalloutHandleState is
1084  // destroyed, but the reference in the lambda keeps it alive until
1085  // the lambda is called and the last reference is released, at which
1086  // time the task is actually added.
1087  // Use the RAII wrapper to make sure that the callout handle state is
1088  // reset when this object goes out of scope. All hook points must do
1089  // it to prevent possible circular dependency between the callout
1090  // handle and its arguments.
1091  std::shared_ptr<ScopedCalloutHandleState> callout_handle_state =
1092  std::make_shared<ScopedCalloutHandleState>(callout_handle);
1093 
1094  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
1095 
1096  // Also pass the corresponding query packet as argument
1097  callout_handle->setArgument("query6", query);
1098 
1099  Lease6CollectionPtr new_leases(new Lease6Collection());
1100  if (!ctx.new_leases_.empty()) {
1101  // Filter out reused leases as they were not committed.
1102  for (auto new_lease : ctx.new_leases_) {
1103  if (new_lease->reuseable_valid_lft_ == 0) {
1104  new_leases->push_back(new_lease);
1105  }
1106  }
1107  }
1108  callout_handle->setArgument("leases6", new_leases);
1109 
1110  Lease6CollectionPtr deleted_leases(new Lease6Collection());
1111 
1112  // Do per IA lists
1113  for (auto const& iac : ctx.ias_) {
1114  if (!iac.old_leases_.empty()) {
1115  for (auto old_lease : iac.old_leases_) {
1116  if (ctx.new_leases_.empty()) {
1117  deleted_leases->push_back(old_lease);
1118  continue;
1119  }
1120  bool in_new = false;
1121  for (auto const& new_lease : ctx.new_leases_) {
1122  if ((new_lease->addr_ == old_lease->addr_) &&
1123  (new_lease->prefixlen_ == old_lease->prefixlen_)) {
1124  in_new = true;
1125  break;
1126  }
1127  }
1128  if (!in_new) {
1129  deleted_leases->push_back(old_lease);
1130  }
1131  }
1132  }
1133  }
1134  callout_handle->setArgument("deleted_leases6", deleted_leases);
1135 
1136  // Get the parking limit. Parsing should ensure the value is present.
1137  uint32_t parked_packet_limit = 0;
1139  getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT);
1140  if (ppl) {
1141  parked_packet_limit = ppl->intValue();
1142  }
1143 
1144  if (parked_packet_limit) {
1145  const auto& parking_lot = ServerHooks::getServerHooks().
1146  getParkingLotPtr("leases6_committed");
1147  if (parking_lot && (parking_lot->size() >= parked_packet_limit)) {
1148  // We can't park it so we're going to throw it on the floor.
1151  .arg(parked_packet_limit)
1152  .arg(query->getLabel());
1153  isc::stats::StatsMgr::instance().addValue("pkt6-receive-drop",
1154  static_cast<int64_t>(1));
1155  rsp.reset();
1156  return;
1157  }
1158  }
1159 
1160  // We proactively park the packet. We'll unpark it without invoking
1161  // the callback (i.e. drop) unless the callout status is set to
1162  // NEXT_STEP_PARK. Otherwise the callback we bind here will be
1163  // executed when the hook library unparks the packet.
1164  HooksManager::park("leases6_committed", query,
1165  [this, callout_handle, query, rsp, callout_handle_state]() mutable {
1166  if (MultiThreadingMgr::instance().getMode()) {
1167  typedef function<void()> CallBack;
1168  boost::shared_ptr<CallBack> call_back =
1169  boost::make_shared<CallBack>(std::bind(&Dhcpv6Srv::sendResponseNoThrow,
1170  this, callout_handle, query, rsp));
1171  callout_handle_state->on_completion_ = [call_back]() {
1172  MultiThreadingMgr::instance().getThreadPool().add(call_back);
1173  };
1174  } else {
1175  processPacketPktSend(callout_handle, query, rsp);
1176  processPacketBufferSend(callout_handle, rsp);
1177  }
1178  });
1179 
1180  try {
1181  // Call all installed callouts
1182  HooksManager::callCallouts(Hooks.hook_index_leases6_committed_,
1183  *callout_handle);
1184  } catch (...) {
1185  // Make sure we don't orphan a parked packet.
1186  HooksManager::drop("leases6_committed", query);
1187  throw;
1188  }
1189 
1190  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
1192  .arg(query->getLabel());
1193  // Since the hook library(ies) are going to do the unparking, then
1194  // reset the pointer to the response to indicate to the caller that
1195  // it should return, as the packet processing will continue via
1196  // the callback.
1197  rsp.reset();
1198  } else {
1199  // Drop the park job on the packet, it isn't needed.
1200  HooksManager::drop("leases6_committed", query);
1201  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1203  .arg(query->getLabel());
1204  rsp.reset();
1205  }
1206  }
1207  }
1208 
1209  // If we have a response prep it for shipment.
1210  if (rsp) {
1211  processPacketPktSend(callout_handle, query, rsp);
1212  }
1213 }
1214 
1215 void
1217  Pkt6Ptr& query, Pkt6Ptr& rsp) {
1218  try {
1219  processPacketPktSend(callout_handle, query, rsp);
1220  processPacketBufferSend(callout_handle, rsp);
1221  } catch (const std::exception& e) {
1223  .arg(e.what());
1224  } catch (...) {
1226  }
1227 }
1228 
1229 void
1231  Pkt6Ptr& query, Pkt6Ptr& rsp) {
1232  if (!rsp) {
1233  return;
1234  }
1235 
1236  // Specifies if server should do the packing
1237  bool skip_pack = false;
1238 
1239  // Server's reply packet now has all options and fields set.
1240  // Options are represented by individual objects, but the
1241  // output wire data has not been prepared yet.
1242  // Execute all callouts registered for packet6_send
1243  if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
1244 
1245  // Use the RAII wrapper to make sure that the callout handle state is
1246  // reset when this object goes out of scope. All hook points must do
1247  // it to prevent possible circular dependency between the callout
1248  // handle and its arguments.
1249  ScopedCalloutHandleState callout_handle_state(callout_handle);
1250 
1251  // Enable copying options from the packets within hook library.
1252  ScopedEnableOptionsCopy<Pkt6> query_resp_options_copy(query, rsp);
1253 
1254  // Pass incoming packet as argument
1255  callout_handle->setArgument("query6", query);
1256 
1257  // Set our response
1258  callout_handle->setArgument("response6", rsp);
1259 
1260  // Call all installed callouts
1261  HooksManager::callCallouts(Hooks.hook_index_pkt6_send_, *callout_handle);
1262 
1263  // Callouts decided to skip the next processing step. The next
1264  // processing step would be to pack the packet (create wire data).
1265  // That step will be skipped if any callout sets skip flag.
1266  // It essentially means that the callout already did packing,
1267  // so the server does not have to do it again.
1268  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
1270  .arg(rsp->getLabel());
1271  skip_pack = true;
1272  }
1273 
1275  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1277  .arg(rsp->getLabel());
1278  rsp.reset();
1279  return;
1280  }
1281  }
1282 
1283  if (!skip_pack) {
1284  try {
1285  rsp->pack();
1286  } catch (const std::exception& e) {
1287  LOG_ERROR(options6_logger, DHCP6_PACK_FAIL).arg(e.what());
1288  return;
1289  }
1290 
1291  }
1292 }
1293 
1294 void
1296  Pkt6Ptr& rsp) {
1297  if (!rsp) {
1298  return;
1299  }
1300 
1301  try {
1302  // Now all fields and options are constructed into output wire buffer.
1303  // Option objects modification does not make sense anymore. Hooks
1304  // can only manipulate wire buffer at this stage.
1305  // Let's execute all callouts registered for buffer6_send
1306  if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
1307 
1308  // Use the RAII wrapper to make sure that the callout handle state is
1309  // reset when this object goes out of scope. All hook points must do
1310  // it to prevent possible circular dependency between the callout
1311  // handle and its arguments.
1312  ScopedCalloutHandleState callout_handle_state(callout_handle);
1313 
1314  // Enable copying options from the packet within hook library.
1315  ScopedEnableOptionsCopy<Pkt6> response6_options_copy(rsp);
1316 
1317  // Pass incoming packet as argument
1318  callout_handle->setArgument("response6", rsp);
1319 
1320  // Call callouts
1321  HooksManager::callCallouts(Hooks.hook_index_buffer6_send_,
1322  *callout_handle);
1323 
1324  // Callouts decided to skip the next processing step. The next
1325  // processing step would be to parse the packet, so skip at this
1326  // stage means drop.
1327  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1328  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1331  .arg(rsp->getLabel());
1332  return;
1333  }
1334 
1335  callout_handle->getArgument("response6", rsp);
1336  }
1337 
1339  .arg(rsp->getLabel())
1340  .arg(rsp->getName())
1341  .arg(static_cast<int>(rsp->getType()))
1342  .arg(rsp->getLocalAddr().isV6Zero() ? "*" : rsp->getLocalAddr().toText())
1343  .arg(rsp->getLocalPort())
1344  .arg(rsp->getRemoteAddr())
1345  .arg(rsp->getRemotePort())
1346  .arg(rsp->getIface());
1347 
1349  .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
1350 
1351  sendPacket(rsp);
1352 
1353  // Update statistics accordingly for sent packet.
1354  processStatsSent(rsp);
1355 
1356  } catch (const std::exception& e) {
1358  }
1359 }
1360 
1361 std::string
1363  stringstream tmp;
1364 
1365  OptionBuffer data = opt->getData();
1366 
1367  bool colon = false;
1368  for (OptionBufferConstIter it = data.begin(); it != data.end(); ++it) {
1369  if (colon) {
1370  tmp << ":";
1371  }
1372  tmp << hex << setw(2) << setfill('0') << static_cast<uint16_t>(*it);
1373  if (!colon) {
1374  colon = true;
1375  }
1376  }
1377 
1378  return tmp.str();
1379 }
1380 
1381 void
1382 Dhcpv6Srv::copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
1383  // Add client-id.
1384  OptionPtr clientid = question->getOption(D6O_CLIENTID);
1385  if (clientid) {
1386  answer->addOption(clientid);
1387  }
1389 
1390  // If this is a relayed message, we need to copy relay information
1391  if (!question->relay_info_.empty()) {
1392  answer->copyRelayInfo(question);
1393  }
1394 
1395 }
1396 
1397 void
1399  const CfgOptionList&) {
1400  // add server-id
1401  answer->addOption(getServerID());
1402 }
1403 
1404 void
1407  CfgOptionList& co_list) {
1408  // Firstly, host specific options.
1409  if (ctx.currentHost() && !ctx.currentHost()->getCfgOption6()->empty()) {
1410  co_list.push_back(ctx.currentHost()->getCfgOption6());
1411  }
1412 
1413  // Secondly, pool specific options. Pools are defined within a subnet, so
1414  // if there is no subnet, there is nothing to do.
1415  if (ctx.subnet_) {
1416  for (auto resource : ctx.allocated_resources_) {
1417  PoolPtr pool =
1418  ctx.subnet_->getPool(resource.getPrefixLength() == 128 ?
1420  resource.getAddress(),
1421  false);
1422  if (pool && !pool->getCfgOption()->empty()) {
1423  co_list.push_back(pool->getCfgOption());
1424  }
1425  }
1426  };
1427 
1428  if (ctx.subnet_) {
1429  // Next, subnet configured options.
1430  if (!ctx.subnet_->getCfgOption()->empty()) {
1431  co_list.push_back(ctx.subnet_->getCfgOption());
1432  }
1433 
1434  // Then, shared network specific options.
1435  SharedNetwork6Ptr network;
1436  ctx.subnet_->getSharedNetwork(network);
1437  if (network && !network->getCfgOption()->empty()) {
1438  co_list.push_back(network->getCfgOption());
1439  }
1440  }
1441 
1442  // Each class in the incoming packet
1443  const ClientClasses& classes = question->getClasses();
1444  for (ClientClasses::const_iterator cclass = classes.cbegin();
1445  cclass != classes.cend(); ++cclass) {
1446  // Find the client class definition for this class
1447  const ClientClassDefPtr& ccdef = CfgMgr::instance().getCurrentCfg()->
1448  getClientClassDictionary()->findClass(*cclass);
1449  if (!ccdef) {
1450  // Not found: the class is built-in or not configured
1451  if (!isClientClassBuiltIn(*cclass)) {
1453  .arg(question->getLabel())
1454  .arg(*cclass);
1455  }
1456  // Skip it
1457  continue;
1458  }
1459 
1460  if (ccdef->getCfgOption()->empty()) {
1461  // Skip classes which don't configure options
1462  continue;
1463  }
1464 
1465  co_list.push_back(ccdef->getCfgOption());
1466  }
1467 
1468  // Last global options
1469  if (!CfgMgr::instance().getCurrentCfg()->getCfgOption()->empty()) {
1470  co_list.push_back(CfgMgr::instance().getCurrentCfg()->getCfgOption());
1471  }
1472 }
1473 
1474 void
1476  const CfgOptionList& co_list) {
1477 
1478  // Unlikely short cut
1479  if (co_list.empty()) {
1480  return;
1481  }
1482 
1483  set<uint16_t> requested_opts;
1484 
1485  // Client requests some options using ORO option. Try to
1486  // get this option from client's message.
1487  boost::shared_ptr<OptionIntArray<uint16_t> > option_oro =
1488  boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >
1489  (question->getOption(D6O_ORO));
1490 
1491  // Get the list of options that client requested.
1492  if (option_oro) {
1493  set<uint16_t> oro_req_opts;
1494  for (uint16_t code : option_oro->getValues()) {
1495  static_cast<void>(oro_req_opts.insert(code));
1496  }
1497  requested_opts = oro_req_opts;
1498  }
1499 
1500  // Iterate on the configured option list to add persistent options
1501  for (CfgOptionList::const_iterator copts = co_list.begin();
1502  copts != co_list.end(); ++copts) {
1503  const OptionContainerPtr& opts = (*copts)->getAll(DHCP6_OPTION_SPACE);
1504  if (!opts) {
1505  continue;
1506  }
1507  // Get persistent options
1508  const OptionContainerPersistIndex& idx = opts->get<2>();
1509  const OptionContainerPersistRange& range = idx.equal_range(true);
1510  for (OptionContainerPersistIndex::const_iterator desc = range.first;
1511  desc != range.second; ++desc) {
1512  // Add the persistent option code to requested options
1513  if (desc->option_) {
1514  uint16_t code = desc->option_->getType();
1515  static_cast<void>(requested_opts.insert(code));
1516  }
1517  }
1518  }
1519 
1520  for (uint16_t opt : requested_opts) {
1521  // Add nothing when it is already there.
1522  if (!answer->getOption(opt)) {
1523  // Iterate on the configured option list
1524  for (CfgOptionList::const_iterator copts = co_list.begin();
1525  copts != co_list.end(); ++copts) {
1526  OptionDescriptor desc = (*copts)->get(DHCP6_OPTION_SPACE, opt);
1527  // Got it: add it and jump to the outer loop
1528  if (desc.option_) {
1529  answer->addOption(desc.option_);
1530  break;
1531  }
1532  }
1533  }
1534  }
1535 
1536  // Special cases for vendor class and options.
1537  if (requested_opts.count(D6O_VENDOR_CLASS) > 0) {
1538  set<uint32_t> vendor_ids;
1539  for (auto opt : answer->getOptions(D6O_VENDOR_CLASS)) {
1540  OptionVendorClassPtr vendor_class;
1541  vendor_class = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
1542  if (vendor_class) {
1543  uint32_t vendor_id = vendor_class->getVendorId();
1544  static_cast<void>(vendor_ids.insert(vendor_id));
1545  }
1546  }
1547  // Iterate on the configured option list
1548  for (CfgOptionList::const_iterator copts = co_list.begin();
1549  copts != co_list.end(); ++copts) {
1550  for (OptionDescriptor desc : (*copts)->getList(DHCP6_OPTION_SPACE,
1551  D6O_VENDOR_CLASS)) {
1552  if (!desc.option_) {
1553  continue;
1554  }
1555  OptionVendorClassPtr vendor_class =
1556  boost::dynamic_pointer_cast<OptionVendorClass>(desc.option_);
1557  if (!vendor_class) {
1558  continue;
1559  }
1560  // Is the vendor id already in the response?
1561  uint32_t vendor_id = vendor_class->getVendorId();
1562  if (vendor_ids.count(vendor_id) > 0) {
1563  continue;
1564  }
1565  // Got it: add it.
1566  answer->addOption(desc.option_);
1567  static_cast<void>(vendor_ids.insert(vendor_id));
1568  }
1569  }
1570  }
1571 
1572  if (requested_opts.count(D6O_VENDOR_OPTS) > 0) {
1573  set<uint32_t> vendor_ids;
1574  for (auto opt : answer->getOptions(D6O_VENDOR_OPTS)) {
1575  OptionVendorPtr vendor_opts;
1576  vendor_opts = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1577  if (vendor_opts) {
1578  uint32_t vendor_id = vendor_opts->getVendorId();
1579  static_cast<void>(vendor_ids.insert(vendor_id));
1580  }
1581  }
1582  // Iterate on the configured option list
1583  for (CfgOptionList::const_iterator copts = co_list.begin();
1584  copts != co_list.end(); ++copts) {
1585  for (OptionDescriptor desc : (*copts)->getList(DHCP6_OPTION_SPACE,
1586  D6O_VENDOR_OPTS)) {
1587  if (!desc.option_) {
1588  continue;
1589  }
1590  OptionVendorPtr vendor_opts =
1591  boost::dynamic_pointer_cast<OptionVendor>(desc.option_);
1592  if (!vendor_opts) {
1593  continue;
1594  }
1595  // Is the vendor id already in the response?
1596  uint32_t vendor_id = vendor_opts->getVendorId();
1597  if (vendor_ids.count(vendor_id) > 0) {
1598  continue;
1599  }
1600  // Got it: add it.
1601  answer->addOption(desc.option_);
1602  static_cast<void>(vendor_ids.insert(vendor_id));
1603  }
1604  }
1605  }
1606 }
1607 
1608 void
1610  Pkt6Ptr& answer,
1612  const CfgOptionList& co_list) {
1613 
1614  // Leave if there is no subnet matching the incoming packet.
1615  // There is no need to log the error message here because
1616  // it will be logged in the assignLease() when it fails to
1617  // pick the suitable subnet. We don't want to duplicate
1618  // error messages in such case.
1619  //
1620  // Also, if there's no options to possibly assign, give up.
1621  if (!ctx.subnet_ || co_list.empty()) {
1622  return;
1623  }
1624 
1625  set<uint32_t> vendor_ids;
1626 
1627  // The server could have provided the option using client classification or
1628  // hooks. If there're vendor info options in the response already, use them.
1629  map<uint32_t, OptionVendorPtr> vendor_rsps;
1630  for (auto opt : answer->getOptions(D6O_VENDOR_OPTS)) {
1631  OptionVendorPtr vendor_rsp;
1632  vendor_rsp = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1633  if (vendor_rsp) {
1634  uint32_t vendor_id = vendor_rsp->getVendorId();
1635  vendor_rsps[vendor_id] = vendor_rsp;
1636  static_cast<void>(vendor_ids.insert(vendor_id));
1637  }
1638  }
1639 
1640  // Next, try to get the vendor-id from the client packet's
1641  // vendor-specific information option (17).
1642  map<uint32_t, OptionVendorPtr> vendor_reqs;
1643  for (auto opt : question->getOptions(D6O_VENDOR_OPTS)) {
1644  OptionVendorPtr vendor_req;
1645  vendor_req = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1646  if (vendor_req) {
1647  uint32_t vendor_id = vendor_req->getVendorId();
1648  vendor_reqs[vendor_id] = vendor_req;
1649  static_cast<void>(vendor_ids.insert(vendor_id));
1650  }
1651  }
1652 
1653  // Finally, try to get the vendor-id from the client packet's vendor-class
1654  // option (16).
1655  for (auto opt : question->getOptions(D6O_VENDOR_CLASS)) {
1656  OptionVendorClassPtr vendor_class;
1657  vendor_class = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
1658  if (vendor_class) {
1659  uint32_t vendor_id = vendor_class->getVendorId();
1660  static_cast<void>(vendor_ids.insert(vendor_id));
1661  }
1662  }
1663 
1664  // If there's no vendor option in either request or response, then there's no way
1665  // to figure out what the vendor-id values are and we give up.
1666  if (vendor_ids.empty()) {
1667  return;
1668  }
1669 
1670  map<uint32_t, set<uint16_t> > requested_opts;
1671 
1672  // Let's try to get ORO within that vendor-option.
1673  // This is specific to vendor-id=4491 (Cable Labs). Other vendors may have
1674  // different policies.
1676  if (vendor_reqs.count(VENDOR_ID_CABLE_LABS) > 0) {
1677  OptionVendorPtr vendor_req = vendor_reqs[VENDOR_ID_CABLE_LABS];
1678  OptionPtr oro_generic = vendor_req->getOption(DOCSIS3_V6_ORO);
1679  if (oro_generic) {
1680  // Vendor ID 4491 makes Kea look at DOCSIS3_V6_OPTION_DEFINITIONS
1681  // when parsing options. Based on that, oro_generic will have been
1682  // created as an OptionUint16Array, but might not be for other
1683  // vendor IDs.
1684  oro = boost::dynamic_pointer_cast<OptionUint16Array>(oro_generic);
1685  }
1686  if (oro) {
1687  set<uint16_t> oro_req_opts;
1688  for (uint16_t code : oro->getValues()) {
1689  static_cast<void>(oro_req_opts.insert(code));
1690  }
1691  requested_opts[VENDOR_ID_CABLE_LABS] = oro_req_opts;
1692  }
1693  }
1694 
1695  // Iterate on the configured option list to add persistent options
1696  for (uint32_t vendor_id : vendor_ids) {
1697  for (CfgOptionList::const_iterator copts = co_list.begin();
1698  copts != co_list.end(); ++copts) {
1699  const OptionContainerPtr& opts = (*copts)->getAll(vendor_id);
1700  if (!opts) {
1701  continue;
1702  }
1703  // Get persistent options
1704  const OptionContainerPersistIndex& idx = opts->get<2>();
1705  const OptionContainerPersistRange& range = idx.equal_range(true);
1706  for (OptionContainerPersistIndex::const_iterator desc = range.first;
1707  desc != range.second; ++desc) {
1708  if (!desc->option_) {
1709  continue;
1710  }
1711  // Add the persistent option code to requested options
1712  uint16_t code = desc->option_->getType();
1713  static_cast<void>(requested_opts[vendor_id].insert(code));
1714  }
1715  }
1716 
1717  // If there is nothing to add don't do anything then with this vendor.
1718  if (requested_opts[vendor_id].empty()) {
1719  continue;
1720  }
1721 
1722  // It's possible that the vendor opts option was inserted already
1723  // by client class or a hook. If that is so, let's use it.
1724  OptionVendorPtr vendor_rsp;
1725  if (vendor_rsps.count(vendor_id) > 0) {
1726  vendor_rsp = vendor_rsps[vendor_id];
1727  } else {
1728  vendor_rsp.reset(new OptionVendor(Option::V6, vendor_id));
1729  }
1730 
1731  // Get the list of options that client requested.
1732  bool added = false;
1733 
1734  for (uint16_t opt : requested_opts[vendor_id]) {
1735  if (!vendor_rsp->getOption(opt)) {
1736  for (CfgOptionList::const_iterator copts = co_list.begin();
1737  copts != co_list.end(); ++copts) {
1738  OptionDescriptor desc = (*copts)->get(vendor_id, opt);
1739  if (desc.option_) {
1740  vendor_rsp->addOption(desc.option_);
1741  added = true;
1742  break;
1743  }
1744  }
1745  }
1746  }
1747 
1748  // If we added some sub-options and the vendor opts option is not in
1749  // the response already, then add it.
1750  if (added && (vendor_rsps.count(vendor_id) == 0)) {
1751  answer->addOption(vendor_rsp);
1752  }
1753  }
1754 }
1755 
1756 bool
1758  try {
1759  switch (pkt->getType()) {
1760  case DHCPV6_SOLICIT:
1761  case DHCPV6_REBIND:
1762  case DHCPV6_CONFIRM:
1764  return (true);
1765 
1766  case DHCPV6_REQUEST:
1767  case DHCPV6_RENEW:
1768  case DHCPV6_RELEASE:
1769  case DHCPV6_DECLINE:
1771  return (true);
1772 
1774  case DHCPV6_DHCPV4_QUERY:
1775  sanityCheck(pkt, OPTIONAL, OPTIONAL);
1776  return (true);
1777 
1778  default:
1781  .arg(static_cast<int>(pkt->getType()))
1782  .arg(pkt->getIface());
1783  }
1784 
1785  } catch (const RFCViolation& e) {
1787  .arg(pkt->getName())
1788  .arg(pkt->getRemoteAddr().toText())
1789  .arg(e.what());
1790 
1791  }
1792 
1793  // Increase the statistic of dropped packets.
1794  StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
1795  return (false);
1796 }
1797 
1798 void
1800  RequirementLevel serverid) {
1801  OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
1802  switch (clientid) {
1803  case MANDATORY: {
1804  if (client_ids.size() != 1) {
1805  isc_throw(RFCViolation, "Exactly 1 client-id option expected in "
1806  << pkt->getName() << ", but " << client_ids.size()
1807  << " received");
1808  }
1809  sanityCheckDUID(client_ids.begin()->second, "client-id");
1810  break;
1811  }
1812  case OPTIONAL:
1813  if (client_ids.size() > 1) {
1814  isc_throw(RFCViolation, "Too many (" << client_ids.size()
1815  << ") client-id options received in " << pkt->getName());
1816  }
1817  if (!client_ids.empty()) {
1818  sanityCheckDUID(client_ids.begin()->second, "client-id");
1819  }
1820  break;
1821 
1822  case FORBIDDEN:
1823  // doesn't make sense - client-id is always allowed
1824  break;
1825  }
1826 
1827  OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
1828  switch (serverid) {
1829  case FORBIDDEN:
1830  if (!server_ids.empty()) {
1831  isc_throw(RFCViolation, "Server-id option was not expected, but "
1832  << server_ids.size() << " received in " << pkt->getName());
1833  }
1834  break;
1835 
1836  case MANDATORY:
1837  if (server_ids.size() != 1) {
1838  isc_throw(RFCViolation, "Invalid number of server-id options received ("
1839  << server_ids.size() << "), exactly 1 expected in message "
1840  << pkt->getName());
1841  }
1842  sanityCheckDUID(server_ids.begin()->second, "server-id");
1843  break;
1844 
1845  case OPTIONAL:
1846  if (server_ids.size() > 1) {
1847  isc_throw(RFCViolation, "Too many (" << server_ids.size()
1848  << ") server-id options received in " << pkt->getName());
1849  }
1850  if (!server_ids.empty()) {
1851  sanityCheckDUID(server_ids.begin()->second, "server-id");
1852  }
1853  }
1854 }
1855 
1856 void Dhcpv6Srv::sanityCheckDUID(const OptionPtr& opt, const std::string& opt_name) {
1857  if (!opt) {
1858  isc_throw(RFCViolation, "Unable to find expected option " << opt_name);
1859  }
1860 
1861  // The client-id or server-id has to have at least 3 bytes of useful data:
1862  // two for duid type and one more for actual duid value.
1863  uint16_t len = opt->len() - opt->getHeaderLen();
1864  if (len < 3 || len > DUID::MAX_DUID_LEN || opt->getData().empty()) {
1865  isc_throw(RFCViolation, "Received invalid DUID for " << opt_name << ", received "
1866  << len << " byte(s). It must be at least 3 and no more than "
1867  << DUID::MAX_DUID_LEN);
1868  }
1869 }
1870 
1871 Subnet6Ptr
1872 Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question, bool& drop) {
1873  const SubnetSelector& selector = CfgSubnets6::initSelector(question);
1874 
1876  getCfgSubnets6()->selectSubnet(selector);
1877 
1878  // Let's execute all callouts registered for subnet6_receive
1879  if (HooksManager::calloutsPresent(Hooks.hook_index_subnet6_select_)) {
1880  CalloutHandlePtr callout_handle = getCalloutHandle(question);
1881 
1882  // Use the RAII wrapper to make sure that the callout handle state is
1883  // reset when this object goes out of scope. All hook points must do
1884  // it to prevent possible circular dependency between the callout
1885  // handle and its arguments.
1886  ScopedCalloutHandleState callout_handle_state(callout_handle);
1887 
1888  // Enable copying options from the packet within hook library.
1889  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(question);
1890 
1891  // Set new arguments
1892  callout_handle->setArgument("query6", question);
1893  callout_handle->setArgument("subnet6", subnet);
1894 
1895  // We pass pointer to const collection for performance reasons.
1896  // Otherwise we would get a non-trivial performance penalty each
1897  // time subnet6_select is called.
1898  callout_handle->setArgument("subnet6collection",
1899  CfgMgr::instance().getCurrentCfg()->
1900  getCfgSubnets6()->getAll());
1901 
1902  // Call user (and server-side) callouts
1903  HooksManager::callCallouts(Hooks.hook_index_subnet6_select_, *callout_handle);
1904 
1905  // Callouts decided to skip this step. This means that no
1906  // subnet will be selected. Packet processing will continue,
1907  // but it will be severely limited (i.e. only global options
1908  // will be assigned)
1909  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
1911  .arg(question->getLabel());
1912  return (Subnet6Ptr());
1913  }
1914 
1915  // Callouts decided to drop the packet. It is a superset of the
1916  // skip case so no subnet will be selected.
1917  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1919  .arg(question->getLabel());
1920  drop = true;
1921  return (Subnet6Ptr());
1922  }
1923 
1924  // Use whatever subnet was specified by the callout
1925  callout_handle->getArgument("subnet6", subnet);
1926  }
1927 
1928  if (subnet) {
1929  // Log at higher debug level that subnet has been found.
1931  .arg(question->getLabel())
1932  .arg(subnet->getID());
1933  // Log detailed information about the selected subnet at the
1934  // lower debug level.
1936  .arg(question->getLabel())
1937  .arg(subnet->toText());
1938 
1939  } else {
1941  .arg(question->getLabel());
1942  }
1943 
1944  return (subnet);
1945 }
1946 
1947 void
1948 Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
1950  // Save the originally selected subnet.
1951  Subnet6Ptr orig_subnet = ctx.subnet_;
1952 
1953  // We need to allocate addresses for all IA_NA options in the client's
1954  // question (i.e. SOLICIT or REQUEST) message.
1955  // @todo add support for IA_TA
1956 
1957  // For the lease allocation it is critical that the client has sent
1958  // DUID. There is no need to check for the presence of the DUID here
1959  // because we have already checked it in the sanityCheck().
1960 
1961  // Now that we have all information about the client, let's iterate over all
1962  // received options and handle IA_NA options one by one and store our
1963  // responses in answer message (ADVERTISE or REPLY).
1964  //
1965  // @todo: IA_TA once we implement support for temporary addresses.
1966  for (const auto& opt : question->options_) {
1967  switch (opt.second->getType()) {
1968  case D6O_IA_NA: {
1969  OptionPtr answer_opt = assignIA_NA(question, ctx,
1970  boost::dynamic_pointer_cast<
1971  Option6IA>(opt.second));
1972  if (answer_opt) {
1973  answer->addOption(answer_opt);
1974  }
1975  break;
1976  }
1977  case D6O_IA_PD: {
1978  OptionPtr answer_opt = assignIA_PD(question, ctx,
1979  boost::dynamic_pointer_cast<
1980  Option6IA>(opt.second));
1981  if (answer_opt) {
1982  answer->addOption(answer_opt);
1983  }
1984  break;
1985  }
1986  default:
1987  break;
1988  }
1989  }
1990 
1991  // Subnet may be modified by the allocation engine, there are things
1992  // we need to do when that happens.
1993  checkDynamicSubnetChange(question, answer, ctx, orig_subnet);
1994 }
1995 
1996 void
1997 Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
2000  DdnsParamsPtr ddns_params = ctx.getDdnsParams();
2001 
2002  // Get Client FQDN Option from the client's message. If this option hasn't
2003  // been included, do nothing.
2004  Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
2005  Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
2006  if (!fqdn) {
2007  if (ddns_params->getEnableUpdates() &&
2008  (ddns_params->getReplaceClientNameMode() == D2ClientConfig::RCM_ALWAYS ||
2009  ddns_params->getReplaceClientNameMode() == D2ClientConfig::RCM_WHEN_NOT_PRESENT)) {
2010  // Fabricate an empty "client" FQDN with flags requesting
2011  // the server do all the updates. The flags will get modified
2012  // below according the configuration options, the name will
2013  // be supplied later on.
2014  fqdn.reset(new Option6ClientFqdn(Option6ClientFqdn::FLAG_S, "",
2017  .arg(question->getLabel());
2018  } else {
2019  // No FQDN so get the lease hostname from the host reservation if
2020  // there is one.
2021  if (ctx.currentHost()) {
2022  ctx.hostname_ = ctx.currentHost()->getHostname();
2023  }
2024 
2025  return;
2026  }
2027  }
2028 
2030  .arg(question->getLabel())
2031  .arg(fqdn->toText());
2032 
2033  // Create the DHCPv6 Client FQDN Option to be included in the server's
2034  // response to a client.
2035  Option6ClientFqdnPtr fqdn_resp(new Option6ClientFqdn(*fqdn));
2036 
2037  // Set the server S, N, and O flags based on client's flags and
2038  // current configuration.
2039  d2_mgr.adjustFqdnFlags<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
2040 
2041  // Get DDNS update direction flags
2043  ctx.rev_dns_update_);
2044 
2045  // If there's a reservation and it has a hostname specified, use it!
2046  if (ctx.currentHost() && !ctx.currentHost()->getHostname().empty()) {
2047  // Add the qualifying suffix.
2048  // After #3765, this will only occur if the suffix is not empty.
2049  fqdn_resp->setDomainName(d2_mgr.qualifyName(ctx.currentHost()->getHostname(),
2050  *ddns_params, true),
2052  } else {
2053  // Adjust the domain name based on domain name value and type sent by
2054  // the client and current configuration.
2055  d2_mgr.adjustDomainName<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
2056  }
2057 
2058  // Once we have the FQDN setup to use it for the lease hostname. This
2059  // only gets replaced later if the FQDN is to be generated from the address.
2060  ctx.hostname_ = fqdn_resp->getDomainName();
2061 
2062  // The FQDN has been processed successfully. Let's append it to the
2063  // response to be sent to a client. Note that the Client FQDN option is
2064  // always sent back to the client if Client FQDN was included in the
2065  // client's message.
2067  .arg(question->getLabel())
2068  .arg(fqdn_resp->toText());
2069  answer->addOption(fqdn_resp);
2070 
2071  // Optionally, call a hook that may override the decisions made
2072  // earlier.
2073  if (HooksManager::calloutsPresent(Hooks.hook_index_ddns6_update_)) {
2074  CalloutHandlePtr callout_handle = getCalloutHandle(question);
2075 
2076  // Use the RAII wrapper to make sure that the callout handle state is
2077  // reset when this object goes out of scope. All hook points must do
2078  // it to prevent possible circular dependency between the callout
2079  // handle and its arguments.
2080  ScopedCalloutHandleState callout_handle_state(callout_handle);
2081 
2082  // Setup the callout arguments.
2083  Subnet6Ptr subnet = ctx.subnet_;
2084  callout_handle->setArgument("query6", question);
2085  callout_handle->setArgument("response6", answer);
2086  callout_handle->setArgument("subnet6", subnet);
2087  callout_handle->setArgument("hostname", ctx.hostname_);
2088  callout_handle->setArgument("fwd-update", ctx.fwd_dns_update_);
2089  callout_handle->setArgument("rev-update", ctx.rev_dns_update_);
2090  callout_handle->setArgument("ddns-params", ddns_params);
2091 
2092  // Call callouts
2093  HooksManager::callCallouts(Hooks.hook_index_ddns6_update_, *callout_handle);
2094 
2095  // Let's get the parameters returned by hook.
2096  string hook_hostname;
2097  bool hook_fwd_dns_update;
2098  bool hook_rev_dns_update;
2099  callout_handle->getArgument("hostname", hook_hostname);
2100  callout_handle->getArgument("fwd-update", hook_fwd_dns_update);
2101  callout_handle->getArgument("rev-update", hook_rev_dns_update);
2102 
2103  // If there's anything changed by the hook, log it and then update the parameters
2104  if ((ctx.hostname_ != hook_hostname) || (ctx.fwd_dns_update_!= hook_fwd_dns_update) ||
2105  (ctx.rev_dns_update_ != hook_rev_dns_update)) {
2107  .arg(ctx.hostname_).arg(hook_hostname)
2108  .arg(ctx.fwd_dns_update_).arg(hook_fwd_dns_update)
2109  .arg(ctx.rev_dns_update_).arg(hook_rev_dns_update);
2110 
2111  // Update the FQDN option in the response.
2112  fqdn_resp = boost::dynamic_pointer_cast<Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
2113  if (fqdn) {
2114  fqdn_resp->setDomainName(hook_hostname, Option6ClientFqdn::FULL);
2115  if (!(hook_fwd_dns_update || hook_rev_dns_update)) {
2116  // Hook disabled updates, Set flags back to client accordingly.
2117  fqdn_resp->setFlag(Option6ClientFqdn::FLAG_S, 0);
2118  fqdn_resp->setFlag(Option6ClientFqdn::FLAG_N, 1);
2119  }
2120  }
2121 
2122  ctx.hostname_ = hook_hostname;
2123  ctx.fwd_dns_update_ = hook_fwd_dns_update;
2124  ctx.rev_dns_update_ = hook_rev_dns_update;
2125  }
2126  }
2127 }
2128 
2129 void
2132  // Don't create NameChangeRequests if DNS updates are disabled.
2133  if (!(ctx.getDdnsParams()->getEnableUpdates())) {
2134  return;
2135  }
2136 
2137  // The response message instance is always required. For instance it
2138  // holds the Client Identifier. It is a programming error if supplied
2139  // message is NULL.
2140  if (!answer) {
2141  isc_throw(isc::Unexpected, "an instance of the object"
2142  << " encapsulating server's message must not be"
2143  << " NULL when creating DNS NameChangeRequest");
2144  }
2145 
2146  // It is likely that client haven't included the FQDN option. In such case,
2147  // FQDN option will be NULL. This is valid state, so we simply return.
2148  Option6ClientFqdnPtr opt_fqdn = boost::dynamic_pointer_cast<
2149  Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
2150  if (!opt_fqdn) {
2151  return;
2152  }
2153 
2154  // Get the update directions that should be performed based on our
2155  // response FQDN flags.
2156  bool do_fwd = false;
2157  bool do_rev = false;
2159  do_fwd, do_rev);
2160 
2161  // Get the Client Id. It is mandatory and a function creating a response
2162  // would have thrown an exception if it was missing. Thus throwing
2163  // Unexpected if it is missing as it is a programming error.
2164  OptionPtr opt_duid = answer->getOption(D6O_CLIENTID);
2165  if (!opt_duid) {
2167  "client identifier is required when creating a new"
2168  " DNS NameChangeRequest");
2169  }
2170  DuidPtr duid = DuidPtr(new DUID(opt_duid->getData()));
2171 
2172  // Get the FQDN in the on-wire format. It will be needed to compute
2173  // DHCID.
2174  OutputBuffer name_buf(1);
2175  opt_fqdn->packDomainName(name_buf);
2176  const uint8_t* name_data = static_cast<const uint8_t*>(name_buf.getData());
2177  // @todo currently D2Dhcid accepts a vector holding FQDN.
2178  // However, it will be faster if we used a pointer name_data.
2179  std::vector<uint8_t> buf_vec(name_data, name_data + name_buf.getLength());
2180  // Compute DHCID from Client Identifier and FQDN.
2181  isc::dhcp_ddns::D2Dhcid dhcid(*duid, buf_vec);
2182 
2183  // Get all IAs from the answer. For each IA, holding an address we will
2184  // create a corresponding NameChangeRequest.
2185  for (auto answer_ia : answer->getOptions(D6O_IA_NA)) {
2188  Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
2189  Option6IAAddr>(answer_ia.second->getOption(D6O_IAADDR));
2190 
2191  // We need an address to create a name-to-address mapping.
2192  // If address is missing for any reason, go to the next IA.
2193  if (!iaaddr) {
2194  continue;
2195  }
2196 
2197  // If the lease for iaaddr is in the list of changed leases, we need
2198  // to determine if the changes included changes to the FQDN. If so
2199  // then we may need to do a CHG_REMOVE.
2200  bool extended_only = false;
2201  for (Lease6Collection::const_iterator l = ctx.currentIA().changed_leases_.begin();
2202  l != ctx.currentIA().changed_leases_.end(); ++l) {
2203 
2204  if ((*l)->addr_ == iaaddr->getAddress()) {
2205  // The address is the same so this must be renewal. If we're not
2206  // always updating on renew, then we only renew if DNS info has
2207  // changed.
2208  if (!ctx.getDdnsParams()->getUpdateOnRenew() &&
2209  ((*l)->hostname_ == opt_fqdn->getDomainName() &&
2210  (*l)->fqdn_fwd_ == do_fwd && (*l)->fqdn_rev_ == do_rev)) {
2211  extended_only = true;
2212  } else {
2213  // Queue a CHG_REMOVE of the old data.
2214  // NCR will only be created if the lease hostname is not
2215  // empty and at least one of the direction flags is true
2216  queueNCR(CHG_REMOVE, *l);
2217  }
2218 
2219  break;
2220  }
2221  }
2222 
2223  if (!(do_fwd || do_rev) || (extended_only)) {
2224  // Flags indicate no updates needed or it was an extension of
2225  // an existing lease with no FQDN changes. In the case of the
2226  // former, the most likely scenario is that we are honoring the
2227  // client's request that no updates be done.
2228  continue;
2229  }
2230 
2231  // Create new NameChangeRequest. Use the domain name from the FQDN.
2232  // This is an FQDN included in the response to the client, so it
2233  // holds a fully qualified domain-name already (not partial).
2234  // Get the IP address from the lease.
2237  do_fwd, do_rev,
2238  opt_fqdn->getDomainName(),
2239  iaaddr->getAddress().toText(),
2240  dhcid, 0, calculateDdnsTtl(iaaddr->getValid()),
2241  ctx.getDdnsParams()->getUseConflictResolution()));
2243  DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr->toText());
2244 
2245  // Post the NCR to the D2ClientMgr.
2247 
2252  return;
2253  }
2254 }
2255 
2256 HWAddrPtr
2258  CfgMACSources mac_sources = CfgMgr::instance().getCurrentCfg()->
2259  getMACSources().get();
2260  HWAddrPtr hwaddr;
2261  for (CfgMACSources::const_iterator it = mac_sources.begin();
2262  it != mac_sources.end(); ++it) {
2263  hwaddr = pkt->getMAC(*it);
2264  if (hwaddr) {
2265  return (hwaddr);
2266  }
2267  }
2268  return (hwaddr);
2269 }
2270 
2271 OptionPtr
2274  boost::shared_ptr<Option6IA> ia) {
2275 
2276  // Check if the client sent us a hint in his IA_NA. Clients may send an
2277  // address in their IA_NA options as a suggestion (e.g. the last address
2278  // they used before).
2279  Option6IAAddrPtr hint_opt =
2280  boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
2282  if (hint_opt) {
2283  hint = hint_opt->getAddress();
2284  }
2285 
2287  .arg(query->getLabel())
2288  .arg(ia->getIAID())
2289  .arg(hint_opt ? hint.toText() : "(no hint)");
2290 
2291  // convenience values
2292  const Subnet6Ptr& subnet = ctx.subnet_;
2293 
2294  // If there is no subnet selected for handling this IA_NA, the only thing left to do is
2295  // to say that we are sorry, but the user won't get an address. As a convenience, we
2296  // use a different status text to indicate that (compare to the same status code,
2297  // but different wording below)
2298  if (!subnet) {
2299  // Create an empty IA_NA option with IAID matching the request.
2300  // Note that we don't use OptionDefinition class to create this option.
2301  // This is because we prefer using a constructor of Option6IA that
2302  // initializes IAID. Otherwise we would have to use setIAID() after
2303  // creation of the option which has some performance implications.
2304  boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2305 
2306  // Insert status code NoAddrsAvail.
2307  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoAddrsAvail,
2308  "Server could not select subnet for"
2309  " this client"));
2310  return (ia_rsp);
2311  }
2312 
2313  // Set per-IA context values.
2314  ctx.createIAContext();
2315  ctx.currentIA().iaid_ = ia->getIAID();
2316  if (hint_opt) {
2317  ctx.currentIA().addHint(hint_opt);
2318  } else {
2319  ctx.currentIA().addHint(hint);
2320  }
2321  ctx.currentIA().type_ = Lease::TYPE_NA;
2322 
2323  // Use allocation engine to pick a lease for this client. Allocation engine
2324  // will try to honor the hint, but it is just a hint - some other address
2325  // may be used instead. If fake_allocation is set to false, the lease will
2326  // be inserted into the LeaseMgr as well.
2327  Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
2328 
2330  Lease6Ptr lease;
2331  if (!leases.empty()) {
2332  lease = *leases.begin();
2333  }
2334 
2335  // Create IA_NA that we will put in the response.
2336  // Do not use OptionDefinition to create option's instance so
2337  // as we can initialize IAID using a constructor.
2338  Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2339 
2340  if (lease) {
2341  // We have a lease! Let's wrap its content into IA_NA option
2342  // with IAADDR suboption.
2343  if (ctx.fake_allocation_) {
2345  .arg(query->getLabel())
2346  .arg(lease->addr_.toText())
2347  .arg(ia->getIAID());
2348  } else if (lease->reuseable_valid_lft_ == 0) {
2350  .arg(query->getLabel())
2351  .arg(lease->addr_.toText())
2352  .arg(ia->getIAID())
2353  .arg(Lease::lifetimeToText(lease->valid_lft_));
2354  } else {
2355  lease->valid_lft_ = lease->reuseable_valid_lft_;
2356  lease->preferred_lft_ = lease->reuseable_preferred_lft_;
2358  .arg(query->getLabel())
2359  .arg(lease->addr_.toText())
2360  .arg(ia->getIAID())
2361  .arg(Lease::lifetimeToText(lease->valid_lft_));
2362  }
2364  .arg(query->getLabel())
2365  .arg(ia->getIAID())
2366  .arg(lease->toText());
2367 
2368  // Set the values for T1 and T2.
2369  setTeeTimes(lease->preferred_lft_, subnet, ia_rsp);
2370 
2371  Option6IAAddrPtr addr(new Option6IAAddr(D6O_IAADDR, lease->addr_,
2372  lease->preferred_lft_,
2373  lease->valid_lft_));
2374  ia_rsp->addOption(addr);
2375 
2376  // It would be possible to insert status code=0(success) as well,
2377  // but this is considered waste of bandwidth as absence of status
2378  // code is considered a success.
2379 
2380  } else {
2381  // Allocation engine did not allocate a lease. The engine logged
2382  // cause of that failure. The only thing left is to insert
2383  // status code to pass the sad news to the client.
2384 
2387  .arg(query->getLabel())
2388  .arg(ia->getIAID());
2389 
2390  ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2392  "Sorry, no address could be"
2393  " allocated."));
2394  }
2395  return (ia_rsp);
2396 }
2397 
2398 OptionPtr
2401  boost::shared_ptr<Option6IA> ia) {
2402 
2403  // Check if the client sent us a hint in his IA_PD. Clients may send an
2404  // address in their IA_PD options as a suggestion (e.g. the last address
2405  // they used before). While the hint consists of a full prefix (prefix +
2406  // length), getting just the prefix is sufficient to identify a lease.
2407  Option6IAPrefixPtr hint_opt =
2408  boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
2410  if (hint_opt) {
2411  hint = hint_opt->getAddress();
2412  }
2413 
2415  .arg(query->getLabel())
2416  .arg(ia->getIAID())
2417  .arg(hint_opt ? hint.toText() : "(no hint)");
2418 
2419  const Subnet6Ptr& subnet = ctx.subnet_;
2420 
2421  // Create IA_PD that we will put in the response.
2422  // Do not use OptionDefinition to create option's instance so
2423  // as we can initialize IAID using a constructor.
2424  boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
2425 
2426  // If there is no subnet selected for handling this IA_PD, the only thing
2427  // left to do is to say that we are sorry, but the user won't get an address.
2428  // As a convenience, we use a different status text to indicate that
2429  // (compare to the same status code, but different wording below)
2430  if (!subnet) {
2431 
2432  // Insert status code NoAddrsAvail.
2433  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoPrefixAvail,
2434  "Sorry, no subnet available."));
2435  return (ia_rsp);
2436  }
2437 
2438  // Set per-IA context values.
2439  ctx.createIAContext();
2440  ctx.currentIA().iaid_ = ia->getIAID();
2441  if (hint_opt) {
2442  ctx.currentIA().addHint(hint_opt);
2443  } else {
2444  ctx.currentIA().addHint(hint, 0);
2445  }
2446  ctx.currentIA().type_ = Lease::TYPE_PD;
2447 
2448  // Use allocation engine to pick a lease for this client. Allocation engine
2449  // will try to honor the hint, but it is just a hint - some other address
2450  // may be used instead. If fake_allocation is set to false, the lease will
2451  // be inserted into the LeaseMgr as well.
2452  Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
2453 
2454  if (!leases.empty()) {
2455 
2456  // Need to retain the shortest preferred lease time to use
2457  // for calculating T1 and T2.
2458  uint32_t min_preferred_lft = (*leases.begin())->preferred_lft_;
2459 
2460  const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
2461  for (Lease6Collection::iterator l = leases.begin();
2462  l != leases.end(); ++l) {
2463 
2464  // We have a lease! Let's wrap its content into IA_PD option
2465  // with IAADDR suboption.
2466  if (ctx.fake_allocation_) {
2468  .arg(query->getLabel())
2469  .arg((*l)->addr_.toText())
2470  .arg(static_cast<int>((*l)->prefixlen_))
2471  .arg(ia->getIAID());
2472  } else if ((*l)->reuseable_valid_lft_ == 0) {
2474  .arg(query->getLabel())
2475  .arg((*l)->addr_.toText())
2476  .arg(static_cast<int>((*l)->prefixlen_))
2477  .arg(ia->getIAID())
2478  .arg(Lease::lifetimeToText((*l)->valid_lft_));
2479  } else {
2480  (*l)->valid_lft_ = (*l)->reuseable_valid_lft_;
2481  (*l)->preferred_lft_ = (*l)->reuseable_preferred_lft_;
2483  .arg(query->getLabel())
2484  .arg((*l)->addr_.toText())
2485  .arg(static_cast<int>((*l)->prefixlen_))
2486  .arg(ia->getIAID())
2487  .arg(Lease::lifetimeToText((*l)->valid_lft_));
2488  }
2489 
2490  // Check for new minimum lease time
2491  if (((*l)->preferred_lft_ > 0) && (min_preferred_lft > (*l)->preferred_lft_)) {
2492  min_preferred_lft = (*l)->preferred_lft_;
2493  }
2494 
2495  boost::shared_ptr<Option6IAPrefix>
2496  addr(new Option6IAPrefix(D6O_IAPREFIX, (*l)->addr_,
2497  (*l)->prefixlen_, (*l)->preferred_lft_,
2498  (*l)->valid_lft_));
2499  ia_rsp->addOption(addr);
2500 
2501  if (pd_exclude_requested) {
2502  // PD exclude option has been requested via ORO, thus we need to
2503  // include it if the pool configuration specifies this option.
2504  Pool6Ptr pool = boost::dynamic_pointer_cast<
2505  Pool6>(subnet->getPool(Lease::TYPE_PD, (*l)->addr_));
2506  if (pool) {
2507  Option6PDExcludePtr pd_exclude_option = pool->getPrefixExcludeOption();
2508  if (pd_exclude_option) {
2509  addr->addOption(pd_exclude_option);
2510  }
2511  }
2512  }
2513  }
2514 
2515  // Set T1 and T2, using the shortest preferred lifetime among the leases.
2516  setTeeTimes(min_preferred_lft, subnet, ia_rsp);
2517 
2518  // It would be possible to insert status code=0(success) as well,
2519  // but this is considered waste of bandwidth as absence of status
2520  // code is considered a success.
2521 
2522  } else {
2523  // Allocation engine did not allocate a lease. The engine logged
2524  // cause of that failure. The only thing left is to insert
2525  // status code to pass the sad news to the client.
2526 
2529  .arg(query->getLabel())
2530  .arg(ia->getIAID());
2531 
2532  ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2534  "Sorry, no prefixes could"
2535  " be allocated."));
2536  }
2537  return (ia_rsp);
2538 }
2539 
2540 OptionPtr
2543  boost::shared_ptr<Option6IA> ia) {
2544 
2546  .arg(query->getLabel())
2547  .arg(ia->getIAID());
2548 
2549  // convenience values
2550  const Subnet6Ptr& subnet = ctx.subnet_;
2551 
2552  // Create empty IA_NA option with IAID matching the request.
2553  Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2554 
2555  if (!subnet) {
2565  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2566  "Sorry, no known leases for this duid/iaid."));
2567  return (ia_rsp);
2568  }
2569 
2570  // Set per-IA context values.
2571  ctx.createIAContext();
2572  ctx.currentIA().iaid_ = ia->getIAID();
2573  ctx.currentIA().type_ = Lease::TYPE_NA;
2574  ctx.currentIA().ia_rsp_ = ia_rsp;
2575 
2576  // Extract the addresses that the client is trying to obtain.
2577  OptionCollection addrs = ia->getOptions();
2578  for (OptionCollection::const_iterator it = addrs.begin();
2579  it != addrs.end(); ++it) {
2580  if (it->second->getType() != D6O_IAADDR) {
2581  continue;
2582  }
2583  Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(it->second);
2584  if (!iaaddr) {
2585  // That's weird. Option code was ok, but the object type was not.
2586  // This should never happen. The only case would be with badly
2587  // mis-implemented hook libraries that insert invalid option objects.
2588  // There's no way to protect against this.
2589  continue;
2590  }
2591  ctx.currentIA().addHint(iaaddr);
2592  }
2593 
2594  Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
2595 
2596  // Ok, now we have the leases extended. We have:
2597  // - what the client tried to renew in ctx.hints_
2598  // - what we actually assigned in leases
2599  // - old leases that are no longer valid in ctx.old_leases_
2600 
2601  // For each IA inserted by the client we have to determine what to do
2602  // about included addresses and notify the client. We will iterate over
2603  // those prefixes and remove those that we have already processed. We
2604  // don't want to remove them from the context, so we need to copy them
2605  // into temporary container.
2607 
2608  // Retains the shortest valid lease time to use
2609  // for calculating T1 and T2.
2610  uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
2611 
2612  // For all leases we have now, add the IAADDR with non-zero lifetimes.
2613  for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
2614  if ((*l)->reuseable_valid_lft_ == 0) {
2616  .arg(query->getLabel())
2617  .arg((*l)->addr_.toText())
2618  .arg(ia->getIAID());
2619  } else {
2620  (*l)->valid_lft_ = (*l)->reuseable_valid_lft_;
2621  (*l)->preferred_lft_ = (*l)->reuseable_preferred_lft_;
2623  .arg(query->getLabel())
2624  .arg((*l)->addr_.toText())
2625  .arg(ia->getIAID())
2626  .arg(Lease::lifetimeToText((*l)->valid_lft_));
2627  }
2628 
2630  (*l)->addr_, (*l)->preferred_lft_, (*l)->valid_lft_));
2631  ia_rsp->addOption(iaaddr);
2632 
2633  // Check for new minimum lease time
2634  if (((*l)->preferred_lft_ > 0) && (min_preferred_lft > (*l)->preferred_lft_)) {
2635  min_preferred_lft = (*l)->preferred_lft_;
2636  }
2637 
2638  // Now remove this prefix from the hints list.
2639  AllocEngine::Resource hint_type((*l)->addr_, (*l)->prefixlen_);
2640  hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
2641  hints.end());
2642  }
2643 
2644  // For the leases that we just retired, send the addresses with 0 lifetimes.
2645  for (Lease6Collection::iterator l = ctx.currentIA().old_leases_.begin();
2646  l != ctx.currentIA().old_leases_.end(); ++l) {
2647 
2648  // Send an address with zero lifetimes only when this lease belonged to
2649  // this client. Do not send it when we're reusing an old lease that belonged
2650  // to someone else.
2651  if (equalValues(query->getClientId(), (*l)->duid_)) {
2653  (*l)->addr_, 0, 0));
2654  ia_rsp->addOption(iaaddr);
2655  }
2656 
2657  // Now remove this address from the hints list.
2658  AllocEngine::Resource hint_type((*l)->addr_, 128);
2659  hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
2660 
2661  // If the new FQDN settings have changed for the lease, we need to
2662  // delete any existing FQDN records for this lease.
2663  if (((*l)->hostname_ != ctx.hostname_) || ((*l)->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2664  ((*l)->fqdn_rev_ != ctx.rev_dns_update_)) {
2667  .arg(query->getLabel())
2668  .arg((*l)->toText())
2669  .arg(ctx.hostname_)
2670  .arg(ctx.rev_dns_update_ ? "true" : "false")
2671  .arg(ctx.fwd_dns_update_ ? "true" : "false");
2672 
2673  queueNCR(CHG_REMOVE, *l);
2674  }
2675  }
2676 
2677  // Finally, if there are any addresses requested that we haven't dealt with
2678  // already, inform the client that he can't have them.
2679  for (AllocEngine::HintContainer::const_iterator hint = hints.begin();
2680  hint != hints.end(); ++hint) {
2682  hint->getAddress(), 0, 0));
2683  ia_rsp->addOption(iaaddr);
2684  }
2685 
2686  if (!leases.empty()) {
2687  // We allocated leases so we need to update T1 and T2.
2688  setTeeTimes(min_preferred_lft, subnet, ia_rsp);
2689  } else {
2690  // The server wasn't able allocate new lease and renew an existing
2691  // lease. In that case, the server sends NoAddrsAvail per RFC 8415.
2692  ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2694  "Sorry, no addresses could be"
2695  " assigned at this time."));
2696  }
2697 
2698  return (ia_rsp);
2699 }
2700 
2701 OptionPtr
2704  boost::shared_ptr<Option6IA> ia) {
2705 
2707  .arg(query->getLabel())
2708  .arg(ia->getIAID());
2709 
2710  const Subnet6Ptr& subnet = ctx.subnet_;
2711  const DuidPtr& duid = ctx.duid_;
2712 
2713  // Let's create a IA_PD response and fill it in later
2714  Option6IAPtr ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
2715 
2716  // If there is no subnet for the particular client, we can't retrieve
2717  // information about client's leases from lease database. We treat this
2718  // as no binding for the client.
2719  if (!subnet) {
2720  // Per RFC 8415, section 18.3.4, if there is no binding and we are
2721  // processing a Renew, the NoBinding status code should be returned.
2722  if (query->getType() == DHCPV6_RENEW) {
2723  // Insert status code NoBinding
2724  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2725  "Sorry, no known PD leases"
2726  " for this duid/iaid."));
2727  return (ia_rsp);
2728 
2729  // Per RFC 8415, section 18.3.5, if there is no binding and we are
2730  // processing Rebind, the message has to be discarded (assuming that
2731  // the server doesn't know if the prefix in the IA_PD option is
2732  // appropriate for the client's link). The exception being thrown
2733  // here should propagate to the main loop and cause the message to
2734  // be discarded.
2735  } else {
2736 
2745  isc_throw(DHCPv6DiscardMessageError, "no subnet found for the"
2746  " client sending Rebind to extend lifetime of the"
2747  " prefix (DUID=" << duid->toText() << ", IAID="
2748  << ia->getIAID() << ")");
2749  }
2750  }
2751 
2752  // Set per-IA context values.
2753  ctx.createIAContext();
2754  ctx.currentIA().iaid_ = ia->getIAID();
2755  ctx.currentIA().type_ = Lease::TYPE_PD;
2756  ctx.currentIA().ia_rsp_ = ia_rsp;
2757 
2758  // Extract prefixes that the client is trying to renew.
2759  OptionCollection addrs = ia->getOptions();
2760  for (OptionCollection::const_iterator it = addrs.begin();
2761  it != addrs.end(); ++it) {
2762  if (it->second->getType() != D6O_IAPREFIX) {
2763  continue;
2764  }
2765  Option6IAPrefixPtr prf = boost::dynamic_pointer_cast<Option6IAPrefix>(it->second);
2766  if (!prf) {
2767  // That's weird. Option code was ok, but the object type was not.
2768  // This should never happen. The only case would be with badly
2769  // mis-implemented hook libraries that insert invalid option objects.
2770  // There's no way to protect against this.
2771  continue;
2772  }
2773 
2774  // Put the client's prefix into the hints list.
2775  ctx.currentIA().addHint(prf);
2776  }
2777 
2778  // Call Allocation Engine and attempt to renew leases. Number of things
2779  // may happen. Leases may be extended, revoked (if the lease is no longer
2780  // valid or reserved for someone else), or new leases may be added.
2781  // Important parameters are:
2782  // - returned container - current valid leases
2783  // - old_leases - leases that used to be, but are no longer valid
2784  // - changed_leases - leases that have FQDN changed (not really important
2785  // in PD context)
2786  Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
2787 
2788  // For each IA inserted by the client we have to determine what to do
2789  // about included prefixes and notify the client. We will iterate over
2790  // those prefixes and remove those that we have already processed. We
2791  // don't want to remove them from the context, so we need to copy them
2792  // into temporary container.
2794 
2795  const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
2796 
2797  // Retains the shortest valid lease time to use
2798  // for calculating T1 and T2.
2799  uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
2800 
2801  for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
2802  if ((*l)->reuseable_valid_lft_ == 0) {
2804  .arg(query->getLabel())
2805  .arg((*l)->addr_.toText())
2806  .arg(static_cast<int>((*l)->prefixlen_))
2807  .arg(ia->getIAID());
2808  } else {
2809  (*l)->valid_lft_ = (*l)->reuseable_valid_lft_;
2810  (*l)->preferred_lft_ = (*l)->reuseable_preferred_lft_;
2812  .arg(query->getLabel())
2813  .arg((*l)->addr_.toText())
2814  .arg(static_cast<int>((*l)->prefixlen_))
2815  .arg(ia->getIAID())
2816  .arg(Lease::lifetimeToText((*l)->valid_lft_));
2817  }
2818 
2820  (*l)->addr_, (*l)->prefixlen_,
2821  (*l)->preferred_lft_, (*l)->valid_lft_));
2822  ia_rsp->addOption(prf);
2823 
2824  if (pd_exclude_requested) {
2825  // PD exclude option has been requested via ORO, thus we need to
2826  // include it if the pool configuration specifies this option.
2827  Pool6Ptr pool = boost::dynamic_pointer_cast<
2828  Pool6>(subnet->getPool(Lease::TYPE_PD, (*l)->addr_));
2829 
2830  if (pool) {
2831  Option6PDExcludePtr pd_exclude_option = pool->getPrefixExcludeOption();
2832  if (pd_exclude_option) {
2833  prf->addOption(pd_exclude_option);
2834  }
2835  }
2836  }
2837 
2838  // Check for new minimum lease time
2839  if (((*l)->preferred_lft_ > 0) && ((*l)->preferred_lft_ < min_preferred_lft)) {
2840  min_preferred_lft = (*l)->preferred_lft_;
2841  }
2842 
2843  // Now remove this prefix from the hints list.
2844  AllocEngine::Resource hint_type((*l)->addr_, (*l)->prefixlen_);
2845  hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
2846  hints.end());
2847  }
2848 
2850  for (Lease6Collection::iterator l = ctx.currentIA().old_leases_.begin();
2851  l != ctx.currentIA().old_leases_.end(); ++l) {
2852 
2853  // Send a prefix with zero lifetimes only when this lease belonged to
2854  // this client. Do not send it when we're reusing an old lease that belonged
2855  // to someone else.
2856  if (equalValues(query->getClientId(), (*l)->duid_)) {
2857  Option6IAPrefixPtr prefix(new Option6IAPrefix(D6O_IAPREFIX, (*l)->addr_,
2858  (*l)->prefixlen_, 0, 0));
2859  ia_rsp->addOption(prefix);
2860  }
2861 
2862  // Now remove this prefix from the hints list.
2863  AllocEngine::Resource hint_type((*l)->addr_, (*l)->prefixlen_);
2864  hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
2865  }
2866 
2867  // Finally, if there are any prefixes requested that we haven't dealt with
2868  // already, inform the client that he can't have them.
2869  for (AllocEngine::HintContainer::const_iterator prefix = hints.begin();
2870  prefix != hints.end(); ++prefix) {
2871 
2872  // Send the prefix with the zero lifetimes only if the prefix
2873  // contains non-zero value. A zero value indicates that the hint was
2874  // for the prefix length.
2875  if (!prefix->getAddress().isV6Zero()) {
2876  OptionPtr prefix_opt(new Option6IAPrefix(D6O_IAPREFIX,
2877  prefix->getAddress(),
2878  prefix->getPrefixLength(),
2879  0, 0));
2880  ia_rsp->addOption(prefix_opt);
2881  }
2882  }
2883 
2884  if (!leases.empty()) {
2885  // We allocated leases so we need to update T1 and T2.
2886  setTeeTimes(min_preferred_lft, subnet, ia_rsp);
2887  } else {
2888  // All is left is to insert the status code.
2889  // The server wasn't able allocate new lease and renew an existing
2890  // lease. In that case, the server sends NoPrefixAvail per RFC 8415.
2891  ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2893  "Sorry, no prefixes could be"
2894  " assigned at this time."));
2895  }
2896 
2897  return (ia_rsp);
2898 }
2899 
2900 void
2903 
2904  // We will try to extend lease lifetime for all IA options in the client's
2905  // Renew or Rebind message.
2907 
2908  // For the lease extension it is critical that the client has sent
2909  // DUID. There is no need to check for the presence of the DUID here
2910  // because we have already checked it in the sanityCheck().
2911 
2912  // Save the originally selected subnet.
2913  Subnet6Ptr orig_subnet = ctx.subnet_;
2914 
2915  for (const auto& opt : query->options_) {
2916  switch (opt.second->getType()) {
2917  case D6O_IA_NA: {
2918  OptionPtr answer_opt = extendIA_NA(query, ctx,
2919  boost::dynamic_pointer_cast<
2920  Option6IA>(opt.second));
2921  if (answer_opt) {
2922  reply->addOption(answer_opt);
2923  }
2924  break;
2925  }
2926 
2927  case D6O_IA_PD: {
2928  OptionPtr answer_opt = extendIA_PD(query, ctx,
2929  boost::dynamic_pointer_cast<
2930  Option6IA>(opt.second));
2931  if (answer_opt) {
2932  reply->addOption(answer_opt);
2933  }
2934  break;
2935  }
2936 
2937  default:
2938  break;
2939  }
2940  }
2941 
2942  // Subnet may be modified by the allocation engine, there are things
2943  // we need to do when that happens.
2944  checkDynamicSubnetChange(query, reply, ctx, orig_subnet);
2945 }
2946 
2947 void
2950 
2951  // We need to release addresses for all IA options in the client's
2952  // RELEASE message.
2953 
2960 
2961  // Let's set the status to be success by default. We can override it with
2962  // error status if needed. The important thing to understand here is that
2963  // the global status code may be set to success only if all IA options were
2964  // handled properly. Therefore the releaseIA_NA and releaseIA_PD options
2965  // may turn the status code to some error, but can't turn it back to success.
2966  int general_status = STATUS_Success;
2967  for (const auto& opt : release->options_) {
2968  Lease6Ptr old_lease;
2969  switch (opt.second->getType()) {
2970  case D6O_IA_NA: {
2971  OptionPtr answer_opt = releaseIA_NA(ctx.duid_, release, general_status,
2972  boost::dynamic_pointer_cast<Option6IA>(opt.second),
2973  old_lease);
2974  if (answer_opt) {
2975  reply->addOption(answer_opt);
2976  }
2977  break;
2978  }
2979  case D6O_IA_PD: {
2980  OptionPtr answer_opt = releaseIA_PD(ctx.duid_, release, general_status,
2981  boost::dynamic_pointer_cast<Option6IA>(opt.second),
2982  old_lease);
2983  if (answer_opt) {
2984  reply->addOption(answer_opt);
2985  }
2986  break;
2987  }
2988  // @todo: add support for IA_TA
2989  default:
2990  // remaining options are stateless and thus ignored in this context
2991  ;
2992  }
2993 
2994  // Store the old lease.
2995  if (old_lease) {
2996  ctx.currentIA().old_leases_.push_back(old_lease);
2997  }
2998  }
2999 
3000  // Include top-level status code as well.
3001  reply->addOption(createStatusCode(*release, general_status,
3002  "Summary status for all processed IA_NAs"));
3003 }
3004 
3005 OptionPtr
3006 Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
3007  int& general_status, boost::shared_ptr<Option6IA> ia,
3008  Lease6Ptr& old_lease) {
3009 
3011  .arg(query->getLabel())
3012  .arg(ia->getIAID());
3013 
3014  // Release can be done in one of two ways:
3015  // Approach 1: extract address from client's IA_NA and see if it belongs
3016  // to this particular client.
3017  // Approach 2: find a subnet for this client, get a lease for
3018  // this subnet/duid/iaid and check if its content matches to what the
3019  // client is asking us to release.
3020  //
3021  // This method implements approach 1.
3022 
3023  // That's our response
3024  boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
3025 
3026  Option6IAAddrPtr release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
3027  (ia->getOption(D6O_IAADDR));
3028  if (!release_addr) {
3029  ia_rsp->addOption(createStatusCode(*query, STATUS_NoBinding,
3030  "You did not include an address in your RELEASE"));
3031  general_status = STATUS_NoBinding;
3032  return (ia_rsp);
3033  }
3034 
3036  release_addr->getAddress());
3037 
3038  if (!lease) {
3039  // client releasing a lease that we don't know about.
3040 
3041  // Insert status code NoBinding.
3042  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3043  "Sorry, no known leases for this duid/iaid, can't release."));
3044  general_status = STATUS_NoBinding;
3045 
3046  return (ia_rsp);
3047  }
3048 
3049  if (!lease->duid_) {
3050  // Something is gravely wrong here. We do have a lease, but it does not
3051  // have mandatory DUID information attached. Someone was messing with our
3052  // database.
3053 
3055  .arg(query->getLabel())
3056  .arg(release_addr->getAddress().toText());
3057 
3058  general_status = STATUS_UnspecFail;
3059  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3060  "Database consistency check failed when trying to RELEASE"));
3061  return (ia_rsp);
3062  }
3063 
3064  if (*duid != *(lease->duid_)) {
3065 
3066  // Sorry, it's not your address. You can't release it.
3068  .arg(query->getLabel())
3069  .arg(release_addr->getAddress().toText())
3070  .arg(lease->duid_->toText());
3071 
3072  general_status = STATUS_NoBinding;
3073  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3074  "This address does not belong to you, you can't release it"));
3075  return (ia_rsp);
3076  }
3077 
3078  if (ia->getIAID() != lease->iaid_) {
3079  // This address belongs to this client, but to a different IA
3081  .arg(query->getLabel())
3082  .arg(release_addr->getAddress().toText())
3083  .arg(lease->iaid_)
3084  .arg(ia->getIAID());
3085  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3086  "This is your address, but you used wrong IAID"));
3087  general_status = STATUS_NoBinding;
3088  return (ia_rsp);
3089  }
3090 
3091  // It is not necessary to check if the address matches as we used
3092  // getLease6(addr) method that is supposed to return a proper lease.
3093 
3094  bool skip = false;
3095  // Execute all callouts registered for packet6_send
3096  if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
3097  CalloutHandlePtr callout_handle = getCalloutHandle(query);
3098 
3099  // Use the RAII wrapper to make sure that the callout handle state is
3100  // reset when this object goes out of scope. All hook points must do
3101  // it to prevent possible circular dependency between the callout
3102  // handle and its arguments.
3103  ScopedCalloutHandleState callout_handle_state(callout_handle);
3104 
3105  // Enable copying options from the packet within hook library.
3106  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
3107 
3108  // Delete all previous arguments
3109  callout_handle->deleteAllArguments();
3110 
3111  // Pass the original packet
3112  callout_handle->setArgument("query6", query);
3113 
3114  // Pass the lease to be updated
3115  callout_handle->setArgument("lease6", lease);
3116 
3117  // Call all installed callouts
3118  HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
3119 
3120  // Callouts decided to skip the next processing step. The next
3121  // processing step would be to send the packet, so skip at this
3122  // stage means "drop response".
3123  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3124  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3125  skip = true;
3127  .arg(query->getLabel());
3128  }
3129  }
3130 
3131  // Ok, we've passed all checks. Let's release this address.
3132  bool success = false; // was the removal operation successful?
3133  bool expired = false; // explicitly expired instead of removed?
3134  auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3135 
3136  // Callout didn't indicate to skip the release process. Let's release
3137  // the lease.
3138  if (!skip) {
3139  // Delete lease only if affinity is disabled.
3140  if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3141  expiration_cfg->getHoldReclaimedTime() &&
3142  lease->valid_lft_ != Lease::INFINITY_LFT) {
3143  // Expire the lease.
3144  lease->valid_lft_ = 0;
3145  lease->preferred_lft_ = 0;
3147  expired = true;
3148  success = true;
3149  } else {
3150  success = LeaseMgrFactory::instance().deleteLease(lease);
3151  }
3152  }
3153 
3154  // Here the success should be true if we removed lease successfully
3155  // and false if skip flag was set or the removal failed for whatever reason
3156 
3157  if (!success) {
3158  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3159  "Server failed to release a lease"));
3160 
3162  .arg(query->getLabel())
3163  .arg(lease->addr_.toText())
3164  .arg(lease->iaid_);
3165  general_status = STATUS_UnspecFail;
3166 
3167  return (ia_rsp);
3168  } else {
3169  old_lease = lease;
3170 
3172  .arg(query->getLabel())
3173  .arg(lease->addr_.toText())
3174  .arg(lease->iaid_);
3175 
3176  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
3177  "Lease released. Thank you, please come again."));
3178 
3179  if (expired) {
3181  .arg(query->getLabel())
3182  .arg(lease->addr_.toText())
3183  .arg(lease->iaid_);
3184  } else {
3186  .arg(query->getLabel())
3187  .arg(lease->addr_.toText())
3188  .arg(lease->iaid_);
3189 
3190  // Need to decrease statistic for assigned addresses.
3191  StatsMgr::instance().addValue(
3192  StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-nas"),
3193  static_cast<int64_t>(-1));
3194 
3195  // Check if a lease has flags indicating that the FQDN update has
3196  // been performed. If so, create NameChangeRequest which removes
3197  // the entries.
3198  queueNCR(CHG_REMOVE, lease);
3199  }
3200 
3201  return (ia_rsp);
3202  }
3203 }
3204 
3205 OptionPtr
3206 Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
3207  int& general_status, boost::shared_ptr<Option6IA> ia,
3208  Lease6Ptr& old_lease) {
3209  // Release can be done in one of two ways:
3210  // Approach 1: extract address from client's IA_NA and see if it belongs
3211  // to this particular client.
3212  // Approach 2: find a subnet for this client, get a lease for
3213  // this subnet/duid/iaid and check if its content matches to what the
3214  // client is asking us to release.
3215  //
3216  // This method implements approach 1.
3217 
3218  // That's our response. We will fill it in as we check the lease to be
3219  // released.
3220  boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
3221 
3222  boost::shared_ptr<Option6IAPrefix> release_prefix =
3223  boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
3224  if (!release_prefix) {
3225  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3226  "You did not include a prefix in your RELEASE"));
3227  general_status = STATUS_NoBinding;
3228  return (ia_rsp);
3229  }
3230 
3232  release_prefix->getAddress());
3233 
3234  if (!lease) {
3235  // Client releasing a lease that we don't know about.
3236 
3237  // Insert status code NoBinding.
3238  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3239  "Sorry, no known leases for this duid/iaid, can't release."));
3240  general_status = STATUS_NoBinding;
3241 
3242  return (ia_rsp);
3243  }
3244 
3245  if (!lease->duid_) {
3246  // Something is gravely wrong here. We do have a lease, but it does not
3247  // have mandatory DUID information attached. Someone was messing with our
3248  // database.
3250  .arg(query->getLabel())
3251  .arg(release_prefix->getAddress().toText())
3252  .arg(static_cast<int>(release_prefix->getLength()));
3253 
3254  general_status = STATUS_UnspecFail;
3255  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3256  "Database consistency check failed when trying to RELEASE"));
3257  return (ia_rsp);
3258  }
3259 
3260  if (*duid != *(lease->duid_)) {
3261  // Sorry, it's not your address. You can't release it.
3263  .arg(query->getLabel())
3264  .arg(release_prefix->getAddress().toText())
3265  .arg(static_cast<int>(release_prefix->getLength()))
3266  .arg(lease->duid_->toText());
3267 
3268  general_status = STATUS_NoBinding;
3269  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3270  "This address does not belong to you, you can't release it"));
3271  return (ia_rsp);
3272  }
3273 
3274  if (ia->getIAID() != lease->iaid_) {
3275  // This address belongs to this client, but to a different IA
3277  .arg(query->getLabel())
3278  .arg(release_prefix->getAddress().toText())
3279  .arg(static_cast<int>(release_prefix->getLength()))
3280  .arg(lease->iaid_)
3281  .arg(ia->getIAID());
3282  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3283  "This is your address, but you used wrong IAID"));
3284  general_status = STATUS_NoBinding;
3285  return (ia_rsp);
3286  }
3287 
3288  // It is not necessary to check if the address matches as we used
3289  // getLease6(addr) method that is supposed to return a proper lease.
3290 
3291  bool skip = false;
3292  // Execute all callouts registered for packet6_send
3293  if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
3294  CalloutHandlePtr callout_handle = getCalloutHandle(query);
3295 
3296  // Use the RAII wrapper to make sure that the callout handle state is
3297  // reset when this object goes out of scope. All hook points must do
3298  // it to prevent possible circular dependency between the callout
3299  // handle and its arguments.
3300  ScopedCalloutHandleState callout_handle_state(callout_handle);
3301 
3302  // Enable copying options from the packet within hook library.
3303  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
3304 
3305  // Pass the original packet
3306  callout_handle->setArgument("query6", query);
3307 
3308  // Pass the lease to be updated
3309  callout_handle->setArgument("lease6", lease);
3310 
3311  // Call all installed callouts
3312  HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
3313 
3314  // Callouts decided to skip the next processing step. The next
3315  // processing step would be to send the packet, so skip at this
3316  // stage means "drop response".
3317  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3318  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3319  skip = true;
3321  .arg(query->getLabel());
3322  }
3323  }
3324 
3325  // Ok, we've passed all checks. Let's release this prefix.
3326  bool success = false; // was the removal operation successful?
3327  bool expired = false; // explicitly expired instead of removed?
3328  auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3329 
3330  // Callout didn't indicate to skip the release process. Let's release
3331  // the lease.
3332  if (!skip) {
3333  // Delete lease only if affinity is disabled.
3334  if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3335  expiration_cfg->getHoldReclaimedTime() &&
3336  lease->valid_lft_ != Lease::INFINITY_LFT) {
3337  // Expire the lease.
3338  lease->valid_lft_ = 0;
3339  lease->preferred_lft_ = 0;
3341  expired = true;
3342  success = true;
3343  } else {
3344  success = LeaseMgrFactory::instance().deleteLease(lease);
3345  }
3346  }
3347 
3348  // Here the success should be true if we removed lease successfully
3349  // and false if skip flag was set or the removal failed for whatever reason
3350 
3351  if (!success) {
3352  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3353  "Server failed to release a lease"));
3354 
3356  .arg(query->getLabel())
3357  .arg(lease->addr_.toText())
3358  .arg(static_cast<int>(lease->prefixlen_))
3359  .arg(lease->iaid_);
3360  general_status = STATUS_UnspecFail;
3361 
3362  } else {
3363  old_lease = lease;
3364 
3366  .arg(query->getLabel())
3367  .arg(lease->addr_.toText())
3368  .arg(static_cast<int>(lease->prefixlen_))
3369  .arg(lease->iaid_);
3370 
3371  ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
3372  "Lease released. Thank you, please come again."));
3373 
3374  if (expired) {
3376  .arg(query->getLabel())
3377  .arg(lease->addr_.toText())
3378  .arg(static_cast<int>(lease->prefixlen_))
3379  .arg(lease->iaid_);
3380  } else {
3382  .arg(query->getLabel())
3383  .arg(lease->addr_.toText())
3384  .arg(static_cast<int>(lease->prefixlen_))
3385  .arg(lease->iaid_);
3386 
3387  // Need to decrease statistic for assigned prefixes.
3388  StatsMgr::instance().addValue(
3389  StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"),
3390  static_cast<int64_t>(-1));
3391  }
3392  }
3393 
3394  return (ia_rsp);
3395 }
3396 
3397 Pkt6Ptr
3399 
3400  Pkt6Ptr solicit = ctx.query_;
3401  Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
3402 
3403  // Handle Rapid Commit option, if present.
3404  if (ctx.subnet_ && ctx.subnet_->getRapidCommit()) {
3405  OptionPtr opt_rapid_commit = solicit->getOption(D6O_RAPID_COMMIT);
3406  if (opt_rapid_commit) {
3407 
3409  .arg(solicit->getLabel());
3410 
3411  // If Rapid Commit has been sent by the client, change the
3412  // response type to Reply and include Rapid Commit option.
3413  response->setType(DHCPV6_REPLY);
3414  response->addOption(opt_rapid_commit);
3415  }
3416  }
3417 
3418  // "Fake" allocation is the case when the server is processing the Solicit
3419  // message without the Rapid Commit option and advertises a lease to
3420  // the client, but doesn't commit this lease to the lease database. If
3421  // the Solicit contains the Rapid Commit option and the server is
3422  // configured to honor the Rapid Commit option, or the client has sent
3423  // the Request message, the lease will be committed to the lease
3424  // database. The type of the server's response may be used to determine
3425  // if this is the fake allocation case or not. When the server sends
3426  // Reply message it means that it is committing leases. Other message
3427  // type (Advertise) means that server is not committing leases (fake
3428  // allocation).
3429  ctx.fake_allocation_ = (response->getType() != DHCPV6_REPLY);
3430 
3431  processClientFqdn(solicit, response, ctx);
3432 
3433  if (MultiThreadingMgr::instance().getMode()) {
3434  // The lease reclamation cannot run at the same time.
3435  ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3436 
3437  assignLeases(solicit, response, ctx);
3438  } else {
3439  assignLeases(solicit, response, ctx);
3440  }
3441 
3443  requiredClassify(solicit, ctx);
3444 
3445  copyClientOptions(solicit, response);
3446  CfgOptionList co_list;
3447  buildCfgOptionList(solicit, ctx, co_list);
3448  appendDefaultOptions(solicit, response, co_list);
3449  appendRequestedOptions(solicit, response, co_list);
3450  appendRequestedVendorOptions(solicit, response, ctx, co_list);
3451 
3452  updateReservedFqdn(ctx, response);
3453 
3454  // Only generate name change requests if sending a Reply as a result
3455  // of receiving Rapid Commit option.
3456  if (response->getType() == DHCPV6_REPLY) {
3457  createNameChangeRequests(response, ctx);
3458  }
3459 
3460  return (response);
3461 }
3462 
3463 Pkt6Ptr
3465 
3466  Pkt6Ptr request = ctx.query_;
3467  Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
3468 
3469  processClientFqdn(request, reply, ctx);
3470 
3471  if (MultiThreadingMgr::instance().getMode()) {
3472  // The lease reclamation cannot run at the same time.
3473  ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3474 
3475  assignLeases(request, reply, ctx);
3476  } else {
3477  assignLeases(request, reply, ctx);
3478  }
3479 
3481  requiredClassify(request, ctx);
3482 
3483  copyClientOptions(request, reply);
3484  CfgOptionList co_list;
3485  buildCfgOptionList(request, ctx, co_list);
3486  appendDefaultOptions(request, reply, co_list);
3487  appendRequestedOptions(request, reply, co_list);
3488  appendRequestedVendorOptions(request, reply, ctx, co_list);
3489 
3490  updateReservedFqdn(ctx, reply);
3491  generateFqdn(reply, ctx);
3492  createNameChangeRequests(reply, ctx);
3493 
3494  return (reply);
3495 }
3496 
3497 Pkt6Ptr
3499 
3500  Pkt6Ptr renew = ctx.query_;
3501  Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
3502 
3503  processClientFqdn(renew, reply, ctx);
3504 
3505  if (MultiThreadingMgr::instance().getMode()) {
3506  // The lease reclamation cannot run at the same time.
3507  ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3508 
3509  extendLeases(renew, reply, ctx);
3510  } else {
3511  extendLeases(renew, reply, ctx);
3512  }
3513 
3515  requiredClassify(renew, ctx);
3516 
3517  copyClientOptions(renew, reply);
3518  CfgOptionList co_list;
3519  buildCfgOptionList(renew, ctx, co_list);
3520  appendDefaultOptions(renew, reply, co_list);
3521  appendRequestedOptions(renew, reply, co_list);
3522  appendRequestedVendorOptions(renew, reply, ctx, co_list);
3523 
3524  updateReservedFqdn(ctx, reply);
3525  generateFqdn(reply, ctx);
3526  createNameChangeRequests(reply, ctx);
3527 
3528  return (reply);
3529 }
3530 
3531 Pkt6Ptr
3533 
3534  Pkt6Ptr rebind = ctx.query_;
3535  Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
3536 
3537  processClientFqdn(rebind, reply, ctx);
3538 
3539  if (MultiThreadingMgr::instance().getMode()) {
3540  // The lease reclamation cannot run at the same time.
3541  ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3542 
3543  extendLeases(rebind, reply, ctx);
3544  } else {
3545  extendLeases(rebind, reply, ctx);
3546  }
3547 
3549  requiredClassify(rebind, ctx);
3550 
3551  copyClientOptions(rebind, reply);
3552  CfgOptionList co_list;
3553  buildCfgOptionList(rebind, ctx, co_list);
3554  appendDefaultOptions(rebind, reply, co_list);
3555  appendRequestedOptions(rebind, reply, co_list);
3556  appendRequestedVendorOptions(rebind, reply, ctx, co_list);
3557 
3558  updateReservedFqdn(ctx, reply);
3559  generateFqdn(reply, ctx);
3560  createNameChangeRequests(reply, ctx);
3561 
3562  return (reply);
3563 }
3564 
3565 Pkt6Ptr
3567 
3568  Pkt6Ptr confirm = ctx.query_;
3570  requiredClassify(confirm, ctx);
3571 
3572  // Get IA_NAs from the Confirm. If there are none, the message is
3573  // invalid and must be discarded. There is nothing more to do.
3574  OptionCollection ias = confirm->getOptions(D6O_IA_NA);
3575  if (ias.empty()) {
3576  return (Pkt6Ptr());
3577  }
3578 
3579  // The server sends Reply message in response to Confirm.
3580  Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
3581  // Make sure that the necessary options are included.
3582  copyClientOptions(confirm, reply);
3583  CfgOptionList co_list;
3584  buildCfgOptionList(confirm, ctx, co_list);
3585  appendDefaultOptions(confirm, reply, co_list);
3586  appendRequestedOptions(confirm, reply, co_list);
3587  appendRequestedVendorOptions(confirm, reply, ctx, co_list);
3588  // Indicates if at least one address has been verified. If no addresses
3589  // are verified it means that the client has sent no IA_NA options
3590  // or no IAAddr options and that client's message has to be discarded.
3591  bool verified = false;
3592  // Check if subnet was selected for the message. If no subnet
3593  // has been selected, the client is not on link.
3594  SubnetPtr subnet = ctx.subnet_;
3595 
3596  // Regardless if the subnet has been selected or not, we will iterate
3597  // over the IA_NA options to check if they hold any addresses. If there
3598  // are no, the Confirm is discarded.
3599  // Check addresses in IA_NA options and make sure they are appropriate.
3600  for (OptionCollection::const_iterator ia = ias.begin();
3601  ia != ias.end(); ++ia) {
3602  const OptionCollection& opts = ia->second->getOptions();
3603  for (OptionCollection::const_iterator opt = opts.begin();
3604  opt != opts.end(); ++opt) {
3605  // Ignore options other than IAAddr.
3606  if (opt->second->getType() == D6O_IAADDR) {
3607  // Check that the address is in range in the subnet selected.
3608  Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
3609  Option6IAAddr>(opt->second);
3610  // If there is subnet selected and the address has been included
3611  // in IA_NA, mark it verified and verify that it belongs to the
3612  // subnet.
3613  if (iaaddr) {
3614  // If at least one address is not in range, then return
3615  // the NotOnLink status code.
3616  if (subnet && !subnet->inRange(iaaddr->getAddress())) {
3617  std::ostringstream status_msg;
3618  status_msg << "Address " << iaaddr->getAddress()
3619  << " is not on link.";
3620  reply->addOption(createStatusCode(*confirm,
3622  status_msg.str()));
3623  return (reply);
3624  }
3625  verified = true;
3626  } else {
3627  isc_throw(Unexpected, "failed to cast the IA Address option"
3628  " to the Option6IAAddrPtr. This is programming"
3629  " error and should be reported");
3630  }
3631  }
3632  }
3633  }
3634 
3635  // It seems that the client hasn't included any addresses in which case
3636  // the Confirm must be discarded.
3637  if (!verified) {
3638  return (Pkt6Ptr());
3639  }
3640 
3641  // If there is a subnet, there were addresses in IA_NA options and the
3642  // addresses where consistent with the subnet then the client is on link.
3643  if (subnet) {
3644  // All addresses in range, so return success.
3645  reply->addOption(createStatusCode(*confirm, STATUS_Success,
3646  "All addresses are on-link"));
3647  } else {
3648  reply->addOption(createStatusCode(*confirm, STATUS_NotOnLink,
3649  "No subnet selected"));
3650  }
3651 
3652  return (reply);
3653 }
3654 
3655 Pkt6Ptr
3657 
3658  Pkt6Ptr release = ctx.query_;
3660  requiredClassify(release, ctx);
3661 
3662  // Create an empty Reply message.
3663  Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
3664 
3665  // Copy client options (client-id, also relay information if present)
3666  copyClientOptions(release, reply);
3667 
3668  // Get the configured option list
3669  CfgOptionList co_list;
3670  // buildCfgOptionList(release, ctx, co_list);
3671  appendDefaultOptions(release, reply, co_list);
3672 
3673  releaseLeases(release, reply, ctx);
3674 
3677 
3678  return (reply);
3679 }
3680 
3681 Pkt6Ptr
3683 
3684  Pkt6Ptr decline = ctx.query_;
3686  requiredClassify(decline, ctx);
3687 
3688  // Create an empty Reply message.
3689  Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
3690 
3691  // Copy client options (client-id, also relay information if present)
3692  copyClientOptions(decline, reply);
3693 
3694  // Get the configured option list
3695  CfgOptionList co_list;
3696  buildCfgOptionList(decline, ctx, co_list);
3697 
3698  // Include server-id
3699  appendDefaultOptions(decline, reply, co_list);
3700 
3701  if (declineLeases(decline, reply, ctx)) {
3702  return (reply);
3703  } else {
3704 
3705  // declineLeases returns false only if the hooks set the next step
3706  // status to DROP. We'll just doing as requested.
3707  return (Pkt6Ptr());
3708  }
3709 }
3710 
3711 bool
3714 
3715  // We need to decline addresses for all IA_NA options in the client's
3716  // DECLINE message.
3717 
3718  // Let's set the status to be success by default. We can override it with
3719  // error status if needed. The important thing to understand here is that
3720  // the global status code may be set to success only if all IA options were
3721  // handled properly. Therefore the declineIA options
3722  // may turn the status code to some error, but can't turn it back to success.
3723  int general_status = STATUS_Success;
3724 
3725  for (const auto& opt : decline->options_) {
3726  switch (opt.second->getType()) {
3727  case D6O_IA_NA: {
3728  OptionPtr answer_opt = declineIA(decline, ctx.duid_, general_status,
3729  boost::dynamic_pointer_cast<Option6IA>(opt.second),
3730  ctx.new_leases_);
3731  if (answer_opt) {
3732 
3733  // We have an answer, let's use it.
3734  reply->addOption(answer_opt);
3735  } else {
3736 
3737  // The only case when declineIA could return NULL is if one of the
3738  // hook callouts set next step status to DROP. We just need to drop
3739  // this packet.
3740  return (false);
3741  }
3742  break;
3743  }
3744  default:
3745  // We don't care for the remaining options
3746  ;
3747  }
3748  }
3749 
3750  return (true);
3751 }
3752 
3753 OptionPtr
3754 Dhcpv6Srv::declineIA(const Pkt6Ptr& decline, const DuidPtr& duid,
3755  int& general_status, boost::shared_ptr<Option6IA> ia,
3756  Lease6Collection& new_leases) {
3757 
3759  .arg(decline->getLabel())
3760  .arg(ia->getIAID());
3761 
3762  // Decline can be done in one of two ways:
3763  // Approach 1: extract address from client's IA_NA and see if it belongs
3764  // to this particular client.
3765  // Approach 2: find a subnet for this client, get a lease for
3766  // this subnet/duid/iaid and check if its content matches to what the
3767  // client is asking us to decline.
3768  //
3769  // This method implements approach 1.
3770 
3771  // That's our response
3772  boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
3773 
3774  const OptionCollection& opts = ia->getOptions();
3775  int total_addrs = 0; // Let's count the total number of addresses.
3776  for (OptionCollection::const_iterator opt = opts.begin(); opt != opts.end();
3777  ++opt) {
3778 
3779  // Let's ignore nested options other than IAADDR (there shouldn't be anything
3780  // else in IA_NA in Decline message, but let's be on the safe side).
3781  if (opt->second->getType() != D6O_IAADDR) {
3782  continue;
3783  }
3784  Option6IAAddrPtr decline_addr = boost::dynamic_pointer_cast<Option6IAAddr>
3785  (opt->second);
3786  if (!decline_addr) {
3787  continue;
3788  }
3789 
3790  total_addrs++;
3791 
3793  decline_addr->getAddress());
3794 
3795  if (!lease) {
3796  // Client trying to decline a lease that we don't know about.
3798  .arg(decline->getLabel()).arg(decline_addr->getAddress().toText());
3799 
3800  // According to RFC 8415, section 18.3.8:
3801  // "For each IA in the Decline message for which the server has no
3802  // binding information, the server adds an IA option using the IAID
3803  // from the Decline message and includes a Status Code option with
3804  // the value NoBinding in the IA option".
3805  setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
3806  "Server does not know about such an address."));
3807 
3808  // In the same section of RFC 8415:
3809  // "The server ignores addresses not assigned to the IAs (though it may"
3810  // choose to log an error if it finds such addresses)."
3811  continue; // There may be other addresses.
3812  }
3813 
3814  if (!lease->duid_) {
3815  // Something is gravely wrong here. We do have a lease, but it does not
3816  // have mandatory DUID information attached. Someone was messing with our
3817  // database.
3818 
3820  .arg(decline->getLabel())
3821  .arg(decline_addr->getAddress().toText());
3822 
3823  ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_UnspecFail,
3824  "Database consistency check failed when attempting Decline."));
3825 
3826  continue;
3827  }
3828 
3829  // Ok, there's a sane lease with an address. Let's check if DUID matches first.
3830  if (*duid != *(lease->duid_)) {
3831 
3832  // Sorry, it's not your address. You can't release it.
3834  .arg(decline->getLabel())
3835  .arg(decline_addr->getAddress().toText())
3836  .arg(lease->duid_->toText());
3837 
3838  ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
3839  "This address does not belong to you, you can't decline it"));
3840 
3841  continue;
3842  }
3843 
3844  // Let's check if IAID matches.
3845  if (ia->getIAID() != lease->iaid_) {
3846  // This address belongs to this client, but to a different IA
3848  .arg(decline->getLabel())
3849  .arg(lease->addr_.toText())
3850  .arg(ia->getIAID())
3851  .arg(lease->iaid_);
3852  setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
3853  "This is your address, but you used wrong IAID"));
3854 
3855  continue;
3856  }
3857 
3858  // Ok, all is good. Decline this lease.
3859  if (!declineLease(decline, lease, ia_rsp)) {
3860  // declineLease returns false only when hook callouts set the next
3861  // step status to drop. We just propagate the bad news here.
3862  return (OptionPtr());
3863 
3864  } else {
3865  new_leases.push_back(lease);
3866  }
3867  }
3868 
3869  if (total_addrs == 0) {
3870  setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
3871  "No addresses sent in IA_NA"));
3872  general_status = STATUS_NoBinding;
3873  }
3874 
3875  return (ia_rsp);
3876 }
3877 
3878 void
3879 Dhcpv6Srv::setStatusCode(boost::shared_ptr<isc::dhcp::Option6IA>& container,
3880  const OptionPtr& status) {
3881  // Let's delete any old status code we may have.
3882  container->delOption(D6O_STATUS_CODE);
3883 
3884  container->addOption(status);
3885 }
3886 
3887 bool
3888 Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
3889  boost::shared_ptr<Option6IA> ia_rsp) {
3890  // We do not want to decrease the assigned-nas at this time. While
3891  // technically a declined address is no longer allocated, the
3892  // primary usage of the assigned-nas statistic is to monitor pool
3893  // utilization. Most people would forget to include declined-nas
3894  // in the calculation, and simply do assigned-nas/total-nas. This
3895  // would have a bias towards under-representing pool utilization,
3896  // if we decreased allocated immediately after receiving DHCPDECLINE,
3897  // rather than later when we recover the address.
3898 
3899  // Let's call lease6_decline hooks if necessary.
3900  if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_decline_)) {
3901  CalloutHandlePtr callout_handle = getCalloutHandle(decline);
3902 
3903  // Use the RAII wrapper to make sure that the callout handle state is
3904  // reset when this object goes out of scope. All hook points must do
3905  // it to prevent possible circular dependency between the callout
3906  // handle and its arguments.
3907  ScopedCalloutHandleState callout_handle_state(callout_handle);
3908 
3909  // Enable copying options from the packet within hook library.
3910  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(decline);
3911 
3912  // Pass the original packet
3913  callout_handle->setArgument("query6", decline);
3914 
3915  // Pass the lease to be updated
3916  callout_handle->setArgument("lease6", lease);
3917 
3918  // Call callouts
3919  HooksManager::callCallouts(Hooks.hook_index_lease6_decline_,
3920  *callout_handle);
3921 
3922  // Callouts decided to SKIP the next processing step. The next
3923  // processing step would be to actually decline the lease, so we'll
3924  // keep the lease as is.
3925  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3927  .arg(decline->getLabel())
3928  .arg(decline->getIface())
3929  .arg(lease->addr_.toText());
3930  return (true);
3931  }
3932 
3933  // Callouts decided to DROP the packet. Let's simply log it and
3934  // return false, so callers will act accordingly.
3935  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
3937  .arg(decline->getLabel())
3938  .arg(decline->getIface())
3939  .arg(lease->addr_.toText());
3940  return (false);
3941  }
3942  }
3943 
3944  Lease6Ptr old_values = boost::make_shared<Lease6>(*lease);
3945 
3946  // @todo: Call hooks.
3947 
3948  // We need to disassociate the lease from the client. Once we move a lease
3949  // to declined state, it is no longer associated with the client in any
3950  // way.
3951  lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
3952 
3953  try {
3955  } catch (const Exception& ex) {
3956  // Update failed.
3958  .arg(decline->getLabel())
3959  .arg(lease->addr_.toText())
3960  .arg(ex.what());
3961  return (false);
3962  }
3963 
3964  // Check if a lease has flags indicating that the FQDN update has
3965  // been performed. If so, create NameChangeRequest which removes
3966  // the entries. This method does all necessary checks.
3967  queueNCR(CHG_REMOVE, old_values);
3968 
3969  // Bump up the subnet-specific statistic.
3970  StatsMgr::instance().addValue(
3971  StatsMgr::generateName("subnet", lease->subnet_id_, "declined-addresses"),
3972  static_cast<int64_t>(1));
3973 
3974  // Global declined addresses counter.
3975  StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
3976 
3977  LOG_INFO(lease6_logger, DHCP6_DECLINE_LEASE).arg(decline->getLabel())
3978  .arg(lease->addr_.toText()).arg(lease->valid_lft_);
3979 
3980  ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_Success,
3981  "Lease declined. Hopefully the next one will be better."));
3982 
3983  return (true);
3984 }
3985 
3986 Pkt6Ptr
3988 
3989  Pkt6Ptr inf_request = ctx.query_;
3990  conditionallySetReservedClientClasses(inf_request, ctx);
3991  requiredClassify(inf_request, ctx);
3992 
3993  // Create a Reply packet, with the same trans-id as the client's.
3994  Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, inf_request->getTransid()));
3995 
3996  // Copy client options (client-id, also relay information if present)
3997  copyClientOptions(inf_request, reply);
3998 
3999  // Build the configured option list for append methods
4000  CfgOptionList co_list;
4001  buildCfgOptionList(inf_request, ctx, co_list);
4002 
4003  // Append default options, i.e. options that the server is supposed
4004  // to put in all messages it sends (server-id for now, but possibly other
4005  // options once we start supporting authentication)
4006  appendDefaultOptions(inf_request, reply, co_list);
4007 
4008  // Try to assign options that were requested by the client.
4009  appendRequestedOptions(inf_request, reply, co_list);
4010 
4011  // Try to assign vendor options that were requested by the client.
4012  appendRequestedVendorOptions(inf_request, reply, ctx, co_list);
4013 
4014  return (reply);
4015 }
4016 
4017 void
4019 
4020  // flags are in transid
4021  // uint32_t flags = dhcp4_query->getTransid();
4022  // do nothing with DHCPV4_QUERY_FLAGS_UNICAST
4023 
4024  // Get the DHCPv4 message option
4025  OptionPtr dhcp4_msg = dhcp4_query->getOption(D6O_DHCPV4_MSG);
4026  if (dhcp4_msg) {
4027  try {
4028  // Forward the whole message to the DHCPv4 server via IPC
4029  Dhcp6to4Ipc::instance().send(dhcp4_query);
4030  } catch (...) {
4031  // Assume the error was already logged
4032  return;
4033  }
4034  }
4035 
4036  // This method does not return anything as we always sent back
4037  // the response via Dhcp6To4Ipc.
4038 }
4039 
4040 void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt) {
4041  OptionVendorClassPtr vclass;
4042  for (auto opt : pkt->getOptions(D6O_VENDOR_CLASS)) {
4043  vclass = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
4044  if (!vclass || vclass->getTuplesNum() == 0) {
4045  continue;
4046  }
4047 
4048  if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) {
4049  pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM);
4050 
4051  } else if (vclass->hasTuple(DOCSIS3_CLASS_EROUTER)) {
4052  pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER);
4053 
4054  } else {
4055  pkt->addClass(VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText());
4056  }
4057  }
4058 }
4059 
4061  // All packets belong to ALL.
4062  pkt->addClass("ALL");
4063 
4064  // First: built-in vendor class processing
4065  classifyByVendor(pkt);
4066 
4067  // Run match expressions on classes not depending on KNOWN/UNKNOWN.
4068  evaluateClasses(pkt, false);
4069 }
4070 
4071 void Dhcpv6Srv::evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known) {
4072  // Note getClientClassDictionary() cannot be null
4073  const ClientClassDictionaryPtr& dict =
4074  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4075  const ClientClassDefListPtr& defs_ptr = dict->getClasses();
4076  for (ClientClassDefList::const_iterator it = defs_ptr->cbegin();
4077  it != defs_ptr->cend(); ++it) {
4078  // Note second cannot be null
4079  const ExpressionPtr& expr_ptr = (*it)->getMatchExpr();
4080  // Nothing to do without an expression to evaluate
4081  if (!expr_ptr) {
4082  continue;
4083  }
4084  // Not the right time if only when required
4085  if ((*it)->getRequired()) {
4086  continue;
4087  }
4088  // Not the right pass.
4089  if ((*it)->getDependOnKnown() != depend_on_known) {
4090  continue;
4091  }
4092  (*it)->test(pkt, expr_ptr);
4093  }
4094 }
4095 
4096 void
4098  const ClientClassDictionaryPtr& dict =
4099  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4100  const ClientClassDefListPtr& defs_ptr = dict->getClasses();
4101  for (auto def : *defs_ptr) {
4102  // Only remove evaluated classes. Other classes can be
4103  // assigned via hooks libraries and we should not remove
4104  // them because there is no way they can be added back.
4105  if (def->getMatchExpr()) {
4106  pkt->classes_.erase(def->getName());
4107  }
4108  }
4109 }
4110 
4111 void
4113  const AllocEngine::ClientContext6& ctx) {
4114  if (ctx.currentHost() && pkt) {
4115  const ClientClasses& classes = ctx.currentHost()->getClientClasses6();
4116  for (ClientClasses::const_iterator cclass = classes.cbegin();
4117  cclass != classes.cend(); ++cclass) {
4118  pkt->addClass(*cclass);
4119  }
4120  }
4121 
4122  const ClientClasses& classes = pkt->getClasses();
4123  if (!classes.empty()) {
4125  .arg(pkt->getLabel())
4126  .arg(classes.toText());
4127  }
4128 }
4129 
4130 void
4132  const AllocEngine::ClientContext6& ctx) {
4133  if (ctx.subnet_) {
4134  SharedNetwork6Ptr shared_network;
4135  ctx.subnet_->getSharedNetwork(shared_network);
4136  if (shared_network) {
4137  ConstHostPtr host = ctx.currentHost();
4138  if (host && (host->getIPv6SubnetID() != SUBNET_ID_GLOBAL)) {
4139  setReservedClientClasses(pkt, ctx);
4140  }
4141  }
4142  }
4143 }
4144 
4145 void
4147  // First collect required classes
4148  ClientClasses classes = pkt->getClasses(true);
4149  Subnet6Ptr subnet = ctx.subnet_;
4150 
4151  if (subnet) {
4152  // Begin by the shared-network
4153  SharedNetwork6Ptr network;
4154  subnet->getSharedNetwork(network);
4155  if (network) {
4156  const ClientClasses& to_add = network->getRequiredClasses();
4157  for (ClientClasses::const_iterator cclass = to_add.cbegin();
4158  cclass != to_add.cend(); ++cclass) {
4159  classes.insert(*cclass);
4160  }
4161  }
4162 
4163  // Followed by the subnet
4164  const ClientClasses& to_add = subnet->getRequiredClasses();
4165  for (ClientClasses::const_iterator cclass = to_add.cbegin();
4166  cclass != to_add.cend(); ++cclass) {
4167  classes.insert(*cclass);
4168  }
4169 
4170  // And finish by pools
4171  for (auto resource : ctx.allocated_resources_) {
4172  PoolPtr pool =
4173  ctx.subnet_->getPool(resource.getPrefixLength() == 128 ?
4175  resource.getAddress(),
4176  false);
4177  if (pool) {
4178  const ClientClasses& to_add = pool->getRequiredClasses();
4179  for (ClientClasses::const_iterator cclass = to_add.cbegin();
4180  cclass != to_add.cend(); ++cclass) {
4181  classes.insert(*cclass);
4182  }
4183  }
4184  }
4185 
4186  // host reservation???
4187  }
4188 
4189  // Run match expressions
4190  // Note getClientClassDictionary() cannot be null
4191  const ClientClassDictionaryPtr& dict =
4192  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4193  for (ClientClasses::const_iterator cclass = classes.cbegin();
4194  cclass != classes.cend(); ++cclass) {
4195  const ClientClassDefPtr class_def = dict->findClass(*cclass);
4196  if (!class_def) {
4198  .arg(*cclass);
4199  continue;
4200  }
4201  const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
4202  // Nothing to do without an expression to evaluate
4203  if (!expr_ptr) {
4205  .arg(*cclass);
4206  continue;
4207  }
4208  // Evaluate the expression which can return false (no match),
4209  // true (match) or raise an exception (error)
4210  try {
4211  bool status = evaluateBool(*expr_ptr, *pkt);
4212  if (status) {
4214  .arg(*cclass)
4215  .arg(status);
4216  // Matching: add the class
4217  pkt->addClass(*cclass);
4218  } else {
4220  .arg(*cclass)
4221  .arg(status);
4222  }
4223  } catch (const Exception& ex) {
4225  .arg(*cclass)
4226  .arg(ex.what());
4227  } catch (...) {
4229  .arg(*cclass)
4230  .arg("get exception?");
4231  }
4232  }
4233 }
4234 
4235 void
4236 Dhcpv6Srv::updateReservedFqdn(AllocEngine::ClientContext6& ctx,
4237  const Pkt6Ptr& answer) {
4238  if (!answer) {
4239  isc_throw(isc::Unexpected, "an instance of the object encapsulating"
4240  " a message must not be NULL when updating reserved FQDN");
4241  }
4242 
4243  Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>
4244  (answer->getOption(D6O_CLIENT_FQDN));
4245 
4246  // If Client FQDN option is not included, there is nothing to do.
4247  if (!fqdn) {
4248  return;
4249  }
4250 
4251  std::string name = fqdn->getDomainName();
4252 
4253  // If there is a host reservation for this client we have to check whether
4254  // this reservation has the same hostname as the hostname currently
4255  // present in the FQDN option. If not, it indicates that the allocation
4256  // engine picked a different subnet (from within a shared network) for
4257  // reservations and we have to send this new value to the client.
4258  if (ctx.currentHost() &&
4259  !ctx.currentHost()->getHostname().empty()) {
4260  std::string new_name = CfgMgr::instance().getD2ClientMgr().
4261  qualifyName(ctx.currentHost()->getHostname(), *ctx.getDdnsParams(), true);
4262 
4263  if (new_name != name) {
4264  fqdn->setDomainName(new_name, Option6ClientFqdn::FULL);
4265 
4266  // Replace previous instance of Client FQDN option.
4267  answer->delOption(D6O_CLIENT_FQDN);
4268  answer->addOption(fqdn);
4269  }
4270  }
4271 }
4272 
4273 void
4274 Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer,
4276  if (!answer) {
4277  isc_throw(isc::Unexpected, "an instance of the object encapsulating"
4278  " a message must not be NULL when generating FQDN");
4279  }
4280 
4283 
4284  // It is likely that client hasn't included the FQDN option. In such case,
4285  // FQDN option will be NULL. Also, there is nothing to do if the option
4286  // is present and conveys the non-empty FQDN.
4287  Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
4288  Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
4289  if (!fqdn || !fqdn->getDomainName().empty()) {
4290  return;
4291  }
4292 
4293  // Get the first IA_NA acquired for the client.
4294  OptionPtr ia = answer->getOption(D6O_IA_NA);
4295  if (!ia) {
4296  return;
4297  }
4298 
4299  // If it has any IAAddr, use the first one to generate unique FQDN.
4300  Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
4301  Option6IAAddr>(ia->getOption(D6O_IAADDR));
4302  if (!iaaddr) {
4303  return;
4304  }
4305  // Get the IPv6 address acquired by the client.
4306  IOAddress addr = iaaddr->getAddress();
4307  std::string generated_name =
4309 
4311  .arg(answer->getLabel())
4312  .arg(generated_name);
4313 
4314  try {
4315  // The lease has been acquired but the FQDN for this lease hasn't
4316  // been updated in the lease database. We now have new FQDN
4317  // generated, so the lease database has to be updated here.
4318  // However, never update lease database for Advertise, just send
4319  // our notion of client's FQDN in the Client FQDN option.
4320  if (answer->getType() != DHCPV6_ADVERTISE) {
4321  Lease6Ptr lease;
4322  for (auto l : ctx.new_leases_) {
4323  if ((l->type_ == Lease::TYPE_NA) && (l->addr_ == addr)) {
4324  lease = l;
4325  break;
4326  }
4327  }
4328  if (lease) {
4329  lease->hostname_ = generated_name;
4330  lease->reuseable_valid_lft_ = 0;
4332 
4333  } else {
4334  isc_throw(isc::Unexpected, "there is no lease in the database "
4335  " for address " << addr << ", so as it is impossible"
4336  " to update FQDN data. This is a programmatic error"
4337  " as the given address is now being handed to the"
4338  " client");
4339  }
4340  }
4341  // Set the generated FQDN in the Client FQDN option.
4342  fqdn->setDomainName(generated_name, Option6ClientFqdn::FULL);
4343 
4344  answer->delOption(D6O_CLIENT_FQDN);
4345  answer->addOption(fqdn);
4346  ctx.hostname_ = generated_name;
4347  } catch (const Exception& ex) {
4349  .arg(answer->getLabel())
4350  .arg(addr.toText())
4351  .arg(ex.what());
4352  }
4353 }
4354 
4355 void
4358  if (d2_mgr.ddnsEnabled()) {
4359  // Updates are enabled, so lets start the sender, passing in
4360  // our error handler.
4361  // This may throw so wherever this is called needs to ready.
4362  d2_mgr.startSender(std::bind(&Dhcpv6Srv::d2ClientErrorHandler,
4363  this, ph::_1, ph::_2));
4364  }
4365 }
4366 
4367 void
4370  if (d2_mgr.ddnsEnabled()) {
4371  // Updates are enabled, so lets stop the sender
4372  d2_mgr.stopSender();
4373  }
4374 }
4375 
4376 void
4381  arg(result).arg((ncr ? ncr->toText() : " NULL "));
4382  // We cannot communicate with kea-dhcp-ddns, suspend further updates.
4386 }
4387 
4388 // Refer to config_report so it will be embedded in the binary
4390 
4391 std::string
4392 Dhcpv6Srv::getVersion(bool extended) {
4393  std::stringstream tmp;
4394 
4395  tmp << VERSION;
4396  if (extended) {
4397  tmp << endl << EXTENDED_VERSION << endl;
4398  tmp << "linked with:" << endl;
4399  tmp << Logger::getVersion() << endl;
4400  tmp << CryptoLink::getVersion() << endl;
4401  tmp << "database:" << endl;
4402 #ifdef HAVE_MYSQL
4403  tmp << MySqlLeaseMgr::getDBVersion() << endl;
4404 #endif
4405 #ifdef HAVE_PGSQL
4406  tmp << PgSqlLeaseMgr::getDBVersion() << endl;
4407 #endif
4409 
4410  // @todo: more details about database runtime
4411  }
4412 
4413  return (tmp.str());
4414 }
4415 
4416 void Dhcpv6Srv::processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp) {
4417 
4418  if (query->relay_info_.empty()) {
4419  // RSOO is inserted by relay agents, nothing to do here if it's
4420  // a direct message.
4421  return;
4422  }
4423 
4424  // Get RSOO configuration.
4425  ConstCfgRSOOPtr cfg_rsoo = CfgMgr::instance().getCurrentCfg()->getCfgRSOO();
4426 
4427  // Let's get over all relays (encapsulation levels). We need to do
4428  // it in the same order as the client packet traversed the relays.
4429  for (int i = query->relay_info_.size(); i > 0 ; --i) {
4430  OptionPtr rsoo_container = query->getRelayOption(D6O_RSOO, i - 1);
4431  if (rsoo_container) {
4432  // There are RSOO options. Let's get through them one by one
4433  // and if it's RSOO-enabled and there's no such option provided yet,
4434  // copy it to the server's response
4435  const OptionCollection& rsoo = rsoo_container->getOptions();
4436  for (OptionCollection::const_iterator opt = rsoo.begin();
4437  opt != rsoo.end(); ++opt) {
4438 
4439  // Echo option if it is RSOO enabled option and there is no such
4440  // option added yet.
4441  if (cfg_rsoo->enabled(opt->second->getType()) &&
4442  !rsp->getOption(opt->second->getType())) {
4443  rsp->addOption(opt->second);
4444  }
4445  }
4446  }
4447  }
4448 }
4449 
4451 
4452  if (query->relay_info_.empty()) {
4453  // No relay agent
4454  return (0);
4455  }
4456 
4457  // Did the last relay agent add a relay-source-port?
4458  if (query->getRelayOption(D6O_RELAY_SOURCE_PORT, 0)) {
4459  // RFC 8357 section 5.2
4460  return (query->getRemotePort());
4461  }
4462 
4463  return (0);
4464 }
4465 
4466 void Dhcpv6Srv::processStatsReceived(const Pkt6Ptr& query) {
4467  // Note that we're not bumping pkt6-received statistic as it was
4468  // increased early in the packet reception code.
4469 
4470  string stat_name = "pkt6-unknown-received";
4471  switch (query->getType()) {
4472  case DHCPV6_SOLICIT:
4473  stat_name = "pkt6-solicit-received";
4474  break;
4475  case DHCPV6_ADVERTISE:
4476  // Should not happen, but let's keep a counter for it
4477  stat_name = "pkt6-advertise-received";
4478  break;
4479  case DHCPV6_REQUEST:
4480  stat_name = "pkt6-request-received";
4481  break;
4482  case DHCPV6_CONFIRM:
4483  stat_name = "pkt6-confirm-received";
4484  break;
4485  case DHCPV6_RENEW:
4486  stat_name = "pkt6-renew-received";
4487  break;
4488  case DHCPV6_REBIND:
4489  stat_name = "pkt6-rebind-received";
4490  break;
4491  case DHCPV6_REPLY:
4492  // Should not happen, but let's keep a counter for it
4493  stat_name = "pkt6-reply-received";
4494  break;
4495  case DHCPV6_RELEASE:
4496  stat_name = "pkt6-release-received";
4497  break;
4498  case DHCPV6_DECLINE:
4499  stat_name = "pkt6-decline-received";
4500  break;
4501  case DHCPV6_RECONFIGURE:
4502  stat_name = "pkt6-reconfigure-received";
4503  break;
4505  stat_name = "pkt6-infrequest-received";
4506  break;
4507  case DHCPV6_DHCPV4_QUERY:
4508  stat_name = "pkt6-dhcpv4-query-received";
4509  break;
4511  // Should not happen, but let's keep a counter for it
4512  stat_name = "pkt6-dhcpv4-response-received";
4513  break;
4514  default:
4515  ; // do nothing
4516  }
4517 
4518  StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
4519 }
4520 
4521 void Dhcpv6Srv::processStatsSent(const Pkt6Ptr& response) {
4522  // Increase generic counter for sent packets.
4523  StatsMgr::instance().addValue("pkt6-sent", static_cast<int64_t>(1));
4524 
4525  // Increase packet type specific counter for packets sent.
4526  string stat_name;
4527  switch (response->getType()) {
4528  case DHCPV6_ADVERTISE:
4529  stat_name = "pkt6-advertise-sent";
4530  break;
4531  case DHCPV6_REPLY:
4532  stat_name = "pkt6-reply-sent";
4533  break;
4535  stat_name = "pkt6-dhcpv4-response-sent";
4536  break;
4537  default:
4538  // That should never happen
4539  return;
4540  }
4541 
4542  StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
4543 }
4544 
4546  return (Hooks.hook_index_buffer6_send_);
4547 }
4548 
4549 bool
4550 Dhcpv6Srv::requestedInORO(const Pkt6Ptr& query, const uint16_t code) const {
4551  OptionUint16ArrayPtr oro =
4552  boost::dynamic_pointer_cast<OptionUint16Array>(query->getOption(D6O_ORO));
4553 
4554  if (oro) {
4555  const std::vector<uint16_t>& codes = oro->getValues();
4556  return (std::find(codes.begin(), codes.end(), code) != codes.end());
4557  }
4558 
4559  return (false);
4560 }
4561 
4563  // Dump all of our current packets, anything that is mid-stream
4564  HooksManager::clearParkingLots();
4565 }
4566 
4568 void
4569 Dhcpv6Srv::setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr& subnet, Option6IAPtr& resp) {
4570  // Default T2 time to zero.
4571  uint32_t t2_time = 0;
4572 
4573  // If T2 is explicitly configured we'll use that value.
4574  if (!subnet->getT2().unspecified()) {
4575  t2_time = subnet->getT2();
4576  } else if (subnet->getCalculateTeeTimes()) {
4577  // Calculating tee times is enabled, so calculate it.
4578  t2_time = static_cast<uint32_t>(round(subnet->getT2Percent() * preferred_lft));
4579  }
4580 
4581  // We allow T2 to be any value.
4582  resp->setT2(t2_time);
4583 
4584  // Default T1 time to zero.
4585  uint32_t t1_time = 0;
4586 
4587  // If T1 is explicitly configured we'll use try value.
4588  if (!subnet->getT1().unspecified()) {
4589  t1_time = subnet->getT1();
4590  } else if (subnet->getCalculateTeeTimes()) {
4591  // Calculating tee times is enabled, so calculate it.
4592  t1_time = static_cast<uint32_t>(round(subnet->getT1Percent() * preferred_lft));
4593  }
4594 
4595  // T1 is sane if it is less than or equal to T2.
4596  if (t1_time < t2_time) {
4597  resp->setT1(t1_time);
4598  } else {
4599  // It's either explicitly 0 or insane, leave it to the client
4600  resp->setT1(0);
4601  }
4602 }
4603 
4604 void
4607  const Subnet6Ptr orig_subnet) {
4608  // If the subnet's are the same there's nothing to do.
4609  if ((!ctx.subnet_) || (!orig_subnet) || (orig_subnet->getID() == ctx.subnet_->getID())) {
4610  return;
4611  }
4612 
4613  // We get the network for logging only. It should always be set as this a dynamic
4614  // change should only happen within shared-networks. Not having one might not be
4615  // an error if a hook changed the subnet?
4616  SharedNetwork6Ptr network;
4617  orig_subnet->getSharedNetwork(network);
4619  .arg(question->getLabel())
4620  .arg(orig_subnet->toText())
4621  .arg(ctx.subnet_->toText())
4622  .arg(network ? network->getName() : "<no network?>");
4623 
4624  // The DDNS parameters may have changed with the subnet, so we need to
4625  // recalculate the client name.
4626 
4627  // Save the current DNS values on the context.
4628  std::string prev_hostname = ctx.hostname_;
4629  bool prev_fwd_dns_update = ctx.fwd_dns_update_;
4630  bool prev_rev_dns_update = ctx.rev_dns_update_;
4631 
4632  // Remove the current FQDN option from the answer.
4633  answer->delOption(D6O_CLIENT_FQDN);
4634 
4635  // Recalculate the client's FQDN. This will replace the FQDN option and
4636  // update the context values for hostname_ and DNS directions.
4637  processClientFqdn(question, answer, ctx);
4638 
4639  // If this is a real allocation and the DNS values changed we need to
4640  // update the leases.
4641  if (!ctx.fake_allocation_ &&
4642  ((prev_hostname != ctx.hostname_) ||
4643  (prev_fwd_dns_update != ctx.fwd_dns_update_) ||
4644  (prev_rev_dns_update != ctx.rev_dns_update_))) {
4645  for (Lease6Collection::const_iterator l = ctx.new_leases_.begin();
4646  l != ctx.new_leases_.end(); ++l) {
4647  (*l)->hostname_ = ctx.hostname_;
4648  (*l)->fqdn_fwd_ = ctx.fwd_dns_update_;
4649  (*l)->fqdn_rev_ = ctx.rev_dns_update_;
4650  (*l)->reuseable_valid_lft_ = 0;
4652  }
4653  }
4654 }
4655 
4656 std::list<std::list<std::string>> Dhcpv6Srv::jsonPathsToRedact() const{
4657  static std::list<std::list<std::string>> const list({
4658  {"config-control", "config-databases", "[]"},
4659  {"hooks-libraries", "[]", "parameters", "*"},
4660  {"hosts-database"},
4661  {"hosts-databases", "[]"},
4662  {"lease-database"},
4663  });
4664  return list;
4665 }
4666 
4667 } // namespace dhcp
4668 } // namespace isc
static const uint8_t FLAG_N
N bit.
Implementation of the mechanisms to control the use of the Configuration Backends by the DHCPv6 serve...
Definition: cb_ctl_dhcp6.h:26
boost::shared_ptr< OptionVendorClass > OptionVendorClassPtr
Defines a pointer to the OptionVendorClass.
RAII class creating a critical section.
static std::string lifetimeToText(uint32_t lifetime)
Print lifetime.
Definition: lease.cc:29
virtual Pkt6Ptr receivePacket(int timeout)
dummy wrapper around IfaceMgr::receive6
Definition: dhcp6_srv.cc:310
const isc::log::MessageID DHCP6_PROCESS_IA_NA_EXTEND
uint32_t calculateDdnsTtl(uint32_t lease_lft)
Calculates TTL for a DNS resource record based on lease life time.
const int DBG_DHCP6_BASIC_DATA
Debug level used to log the traces with some basic data.
Definition: dhcp6_log.h:43
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
const isc::log::MessageID DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST
uint16_t server_port_
UDP port number on which server listens.
Definition: dhcp6_srv.h:1130
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
const isc::log::MessageID DHCP6_DECLINE_PROCESS_IA
Pkt6Ptr processInfRequest(AllocEngine::ClientContext6 &ctx)
Processes incoming Information-request message.
Definition: dhcp6_srv.cc:3987
const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_SKIP
static uint16_t client_port
Definition: dhcp6to4_ipc.h:51
Option descriptor.
Definition: cfg_option.h:46
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition: classify.cc:55
uint32_t getVendorId() const
Returns enterprise identifier.
Definition: option_vendor.h:87
int run()
Main server processing loop.
Definition: dhcp6_srv.cc:589
volatile bool shutdown_
Indicates if shutdown is in progress.
Definition: dhcp6_srv.h:1168
const isc::log::MessageID DHCP6_SUBNET_DYNAMICALLY_CHANGED
const isc::log::MessageID DHCP6_RAPID_COMMIT
const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_DUID
void classifyPacket(const Pkt6Ptr &pkt)
Assigns incoming packet to zero or more classes.
Definition: dhcp6_srv.cc:4060
HWAddrPtr hwaddr_
Hardware/MAC address (if available, may be NULL)
Definition: alloc_engine.h:252
Defines a single hint.
Definition: alloc_engine.h:83
isc::log::Logger packet6_logger(DHCP6_PACKET_LOGGER_NAME)
Logger for processed packets.
Definition: dhcp6_log.h:100
Represents DHCPv6 Client FQDN Option (code 39).
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:1856
isc::log::Logger ddns6_logger(DHCP6_DDNS_LOGGER_NAME)
Logger for Hostname or FQDN processing.
Definition: dhcp6_log.h:112
const isc::log::MessageID DHCP6_SRV_CONSTRUCT_ERROR
const isc::log::MessageID DHCP6_CLASS_UNTESTABLE
Lease6Collection changed_leases_
A pointer to any leases that have changed FQDN information.
Definition: alloc_engine.h:323
void appendDefaultOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, const CfgOptionList &co_list)
Appends default options to server&#39;s answer.
Definition: dhcp6_srv.cc:1398
static std::string getDBVersion()
Local version of getDBVersion() class method.
isc::log::Logger options6_logger(DHCP6_OPTIONS_LOGGER_NAME)
Logger for options parser.
Definition: dhcp6_log.h:106
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:69
const isc::log::MessageID DHCP6_LEASE_ALLOC
const isc::log::MessageID DHCP6_CLASS_ASSIGNED
const isc::log::MessageID DHCP6_PD_LEASE_ADVERT
HintContainer hints_
Client&#39;s hints.
Definition: alloc_engine.h:306
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
static std::string getVersion(bool extended)
returns Kea version on stdout and exit.
Definition: dhcp6_srv.cc:4392
void shutdown() override
Instructs the server to shut down.
Definition: dhcp6_srv.cc:305
virtual void sendPacket(const Pkt6Ptr &pkt)
dummy wrapper around IfaceMgr::send()
Definition: dhcp6_srv.cc:314
const isc::log::MessageID DHCP6_SUBNET_SELECTED
const isc::log::MessageID DHCP6_OPEN_SOCKET
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID
boost::shared_ptr< Pool6 > Pool6Ptr
a pointer an IPv6 Pool
Definition: pool.h:298
const isc::log::MessageID DHCP6_LEASE_ADVERT_FAIL
Exception thrown during option unpacking This exception is thrown when an error has occurred...
Definition: option.h:52
void releaseLeases(const Pkt6Ptr &release, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to release received addresses.
Definition: dhcp6_srv.cc:2948
void adjustDomainName(const T &fqdn, T &fqdn_resp, const DdnsParams &ddns_params)
Set server FQDN name based on configuration and a given FQDN.
static void destroy()
Destroy lease manager.
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:315
const isc::log::MessageID DHCP6_LEASE_RENEW
const isc::log::MessageID DHCP6_LEASE_DATA
static Dhcp6to4Ipc & instance()
Returns pointer to the sole instance of Dhcp6to4Ipc.
Definition: dhcp6to4_ipc.cc:34
An abstract API for lease database.
const isc::log::MessageID DHCP6_RELEASE_NA_FAIL
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
void extendLeases(const Pkt6Ptr &query, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to extend the lifetime of IAs.
Definition: dhcp6_srv.cc:2901
isc::log::Logger lease6_logger(DHCP6_LEASE_LOGGER_NAME)
Logger for lease allocation logic.
Definition: dhcp6_log.h:117
void startSender(D2ClientErrorHandler error_handler, isc::asiolink::IOService &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:476
#define DOCSIS3_V6_ORO
void processRSOO(const Pkt6Ptr &query, const Pkt6Ptr &rsp)
Processes Relay-supplied options, if present.
Definition: dhcp6_srv.cc:4416
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const int DBG_DHCP6_HOOKS
Debug level used to trace hook related operations.
Definition: dhcp6_log.h:34
boost::shared_ptr< const CfgRSOO > ConstCfgRSOOPtr
Pointer to the const object.
Definition: cfg_rsoo.h:74
const isc::log::MessageID DHCP6_DECLINE_FAIL_NO_LEASE
const int DBG_DHCP6_BASIC
Debug level used to trace basic operations within the code.
Definition: dhcp6_log.h:31
Represents a DHCPv6 packet.
Definition: pkt6.h:44
static void processStatsSent(const Pkt6Ptr &response)
Updates statistics for transmitted packets.
Definition: dhcp6_srv.cc:4521
const isc::log::MessageID DHCP6_RELEASE_NA_EXPIRED
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
the lease contains IPv6 prefix (for prefix delegation)
Definition: lease.h:49
const isc::log::MessageID DHCP6_FLEX_ID
void appendRequestedOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, const CfgOptionList &co_list)
Appends requested options to server&#39;s answer.
Definition: dhcp6_srv.cc:1475
const isc::log::MessageID DHCP6_LEASE_PD_WITHOUT_DUID
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
Holds information about DHCP service enabling status.
Definition: network_state.h:70
bool fwd_dns_update_
A boolean value which indicates that server takes responsibility for the forward DNS Update for this ...
Definition: alloc_engine.h:268
STL namespace.
DuidPtr duid_
Client identifier.
Definition: alloc_engine.h:249
OptionPtr assignIA_NA(const isc::dhcp::Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Processes IA_NA option (and assigns addresses if necessary).
Definition: dhcp6_srv.cc:2272
const isc::log::MessageID DHCP6_DDNS_GENERATE_FQDN
OptionPtr extendIA_PD(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Extends lifetime of the prefix.
Definition: dhcp6_srv.cc:2702
const isc::log::MessageID DHCP6_PACKET_PROCESS_STD_EXCEPTION
OptionPtr extendIA_NA(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Extends lifetime of the specific IA_NA option.
Definition: dhcp6_srv.cc:2541
const isc::log::MessageID DHCP6_SUBNET_DATA
static SubnetSelector initSelector(const Pkt6Ptr &query)
Build selector from a client&#39;s message.
void evaluateClasses(const Pkt6Ptr &pkt, bool depend_on_known)
Evaluate classes.
Definition: dhcp6_srv.cc:4071
const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_DROP
static std::string duidToString(const OptionPtr &opt)
converts DUID to text Converts content of DUID option to a text representation, e.g.
Definition: dhcp6_srv.cc:1362
boost::shared_ptr< Pool > PoolPtr
a pointer to either IPv4 or IPv6 Pool
Definition: pool.h:491
const isc::log::MessageID DHCP6_ADD_GLOBAL_STATUS_CODE
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:212
const isc::log::MessageID DHCP6_DECLINE_FAIL_LEASE_WITHOUT_DUID
const isc::log::MessageID DHCP6_PACKET_RECEIVED
void processPacketBufferSend(hooks::CalloutHandlePtr &callout_handle, Pkt6Ptr &rsp)
Executes buffer6_send callout and sends the response.
Definition: dhcp6_srv.cc:1295
int getExitValue()
Fetches the exit value.
Definition: daemon.h:220
DuidPtr get()
Returns current DUID.
const isc::log::MessageID DHCP6_HOOK_LEASE6_RELEASE_PD_SKIP
const isc::log::MessageID DHCP6_DDNS_GENERATED_FQDN_UPDATE_FAIL
const int DBGLVL_PKT_HANDLING
This debug level is reserved for logging the details of packet handling, such as dropping the packet ...
Definition: log_dbglevels.h:58
const_iterator cbegin() const
Iterators to the first element.
Definition: classify.h:152
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition: pkt6.cc:726
const isc::log::MessageID DHCP6_PACKET_QUEUE_FULL
RAII object enabling copying options retrieved from the packet.
Definition: pkt.h:40
void sendRequest(dhcp_ddns::NameChangeRequestPtr &ncr)
Send the given NameChangeRequests to kea-dhcp-ddns.
const isc::log::MessageID DHCP6_LEASE_REUSE
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const isc::log::MessageID DHCP6_PACKET_PROCESS_EXCEPTION
static StatsMgr & instance()
Statistics Manager accessor method.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
Holds DUID (DHCPv6 Unique Identifier)
Definition: duid.h:27
const char *const * dhcp6_config_report
Definition: dhcp6_srv.cc:4389
const isc::log::MessageID DHCP6_PACKET_DROP_DHCP_DISABLED
Statistics Manager class.
boost::shared_ptr< DdnsParams > DdnsParamsPtr
Defines a pointer for DdnsParams instances.
Definition: srv_config.h:172
const isc::log::MessageID DHCP6_PACKET_OPTIONS_SKIPPED
void setReservedClientClasses(const Pkt6Ptr &pkt, const AllocEngine::ClientContext6 &ctx)
Assigns classes retrieved from host reservation database.
Definition: dhcp6_srv.cc:4112
bool send(const Pkt6Ptr &pkt)
Sends an IPv6 packet.
Definition: iface_mgr.cc:1126
void setDomainName(const std::string &domain_name, const DomainNameType domain_name_type)
Set new domain-name.
const isc::log::MessageID DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP
const OptionCollection & getOptions() const
Returns all encapsulated options.
Definition: option.h:347
Subnet selector used to specify parameters used to select a subnet.
static std::string getDBVersion()
Class method to return extended version info This class method must be redeclared and redefined in de...
Definition: lease_mgr.cc:360
boost::shared_ptr< Option6IA > Option6IAPtr
A pointer to the Option6IA object.
Definition: option6_ia.h:17
This class represents Status Code option (13) from RFC 8415.
bool early_global_reservations_lookup_
Indicates if early global reservation is enabled.
Definition: alloc_engine.h:238
const isc::log::MessageID DHCP6_RELEASE_PD
void conditionallySetReservedClientClasses(const Pkt6Ptr &pkt, const AllocEngine::ClientContext6 &ctx)
Assigns classes retrieved from host reservation database if they haven&#39;t been yet set...
Definition: dhcp6_srv.cc:4131
void assignLeases(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Assigns leases.
Definition: dhcp6_srv.cc:1948
const isc::log::MessageID DHCP6_PD_LEASE_REUSE
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
const isc::log::MessageID DHCP6_PACKET_DROP_UNICAST
const isc::log::MessageID DHCP6_PROCESS_IA_NA_RELEASE
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
const isc::log::MessageID DHCP6_SRV_UNLOAD_LIBRARIES_ERROR
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
ClientClassContainer::const_iterator const_iterator
Type of iterators.
Definition: classify.h:112
const isc::log::MessageID DHCP6_NO_INTERFACES
Subnet6Ptr subnet_
Subnet selected for the client by the server.
Definition: alloc_engine.h:241
Pkt6Ptr processRebind(AllocEngine::ClientContext6 &ctx)
Processes incoming Rebind message.
Definition: dhcp6_srv.cc:3532
ResourceContainer allocated_resources_
Holds addresses and prefixes allocated for all IAs.
Definition: alloc_engine.h:285
const isc::log::MessageID DHCP6_SHUTDOWN_REQUEST
Definition: edns.h:19
const isc::log::MessageID DHCP6_PACKET_DROP_PARSE_FAIL
Pool information for IPv6 addresses and prefixes.
Definition: pool.h:307
Forward declaration to OptionIntArray.
const int DBG_DHCP6_START
Debug level used to log information during server startup.
Definition: dhcp6_log.h:22
OptionPtr getServerID()
Returns server-identifier option.
Definition: dhcp6_srv.h:135
Option6PDExcludePtr getPrefixExcludeOption() const
Returns instance of the pool specific Prefix Exclude option.
Definition: pool.h:441
std::list< ConstCfgOptionPtr > CfgOptionList
Const pointer list.
Definition: cfg_option.h:751
const isc::log::MessageID DHCP6_RELEASE_NA
const isc::log::MessageID DHCP6_RELEASE_PD_FAIL
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED
const isc::log::MessageID DHCP6_LEASE_NA_WITHOUT_DUID
Pkt6Ptr processSolicit(AllocEngine::ClientContext6 &ctx)
Processes incoming Solicit and returns response.
Definition: dhcp6_srv.cc:3398
const char * DOCSIS3_CLASS_MODEM
DOCSIS3.0 compatible cable modem.
Definition: libdhcp++.cc:82
virtual std::string toText(int indent=0) const
Returns string representation of the option.
std::string getIdentifierAsText() const
Returns host identifier in a textual form.
Definition: host.cc:270
boost::shared_ptr< Option6ClientFqdn > Option6ClientFqdnPtr
A pointer to the Option6ClientFqdn object.
static HWAddrPtr getMAC(const Pkt6Ptr &pkt)
Attempts to get a MAC/hardware address using configured sources.
Definition: dhcp6_srv.cc:2257
void processPacketPktSend(hooks::CalloutHandlePtr &callout_handle, Pkt6Ptr &query, Pkt6Ptr &rsp)
Executes pkt6_send callout.
Definition: dhcp6_srv.cc:1230
const isc::log::MessageID DHCP6_RELEASE_PD_EXPIRED
const isc::log::MessageID DHCP6_CLASS_UNDEFINED
const isc::log::MessageID DHCP6_DDNS_RECEIVE_FQDN
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
Definition: alloc_engine.h:263
const isc::log::MessageID DHCP6_HOOK_BUFFER_RCVD_DROP
RequirementLevel
defines if certain option may, must or must not appear
Definition: dhcp6_srv.h:74
std::vector< IAContext > ias_
Container holding IA specific contexts.
Definition: alloc_engine.h:380
Context information for the DHCPv6 leases allocation.
Definition: alloc_engine.h:218
bool testServerID(const Pkt6Ptr &pkt)
Compare received server id with our server id.
Definition: dhcp6_srv.cc:319
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
void setStatusCode(boost::shared_ptr< Option6IA > &container, const OptionPtr &status)
A simple utility method that sets the status code.
Definition: dhcp6_srv.cc:3879
void processPacketAndSendResponseNoThrow(Pkt6Ptr &query)
Process a single incoming DHCPv6 packet and sends the response.
Definition: dhcp6_srv.cc:704
const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS
static std::string getDBVersion()
Local version of getDBVersion() class method.
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:290
const isc::log::MessageID DHCP6_HOOK_DECLINE_SKIP
A generic exception that is thrown when an unexpected error condition occurs.
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
void requiredClassify(const Pkt6Ptr &pkt, AllocEngine::ClientContext6 &ctx)
Assigns incoming packet to zero or more classes (required pass).
Definition: dhcp6_srv.cc:4146
const isc::log::MessageID DHCP6_HOOK_BUFFER_SEND_SKIP
void appendRequestedVendorOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, const CfgOptionList &co_list)
Appends requested vendor options to server&#39;s answer.
Definition: dhcp6_srv.cc:1609
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
Wrapper class around callout handle which automatically resets handle&#39;s state.
const isc::log::MessageID DHCP6_QUERY_DATA
static int getHookIndexBuffer6Send()
Returns the index of the buffer6_send hook.
Definition: dhcp6_srv.cc:4545
const isc::log::MessageID DHCP6_SUBNET_SELECTION_FAILED
void buildCfgOptionList(const Pkt6Ptr &question, AllocEngine::ClientContext6 &ctx, CfgOptionList &co_list)
Build the configured option list.
Definition: dhcp6_srv.cc:1405
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:3006
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition: option.cc:199
bool sanityCheck(const Pkt6Ptr &pkt)
Verifies if specified packet meets RFC requirements.
Definition: dhcp6_srv.cc:1757
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
Option6IAPtr ia_rsp_
A pointer to the IA_NA/IA_PD option to be sent in response.
Definition: alloc_engine.h:332
const isc::log::MessageID DHCP6_PACKET_DROP_SERVERID_MISMATCH
const isc::log::MessageID DHCP6_ADD_STATUS_CODE_FOR_IA
Flexible host identifier.
Definition: host.h:312
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
const char * DOCSIS3_CLASS_EROUTER
The class as specified in vendor-class option by the devices.
Definition: libdhcp++.cc:85
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
virtual ~Dhcpv6Srv()
Destructor. Used during DHCPv6 service shutdown.
Definition: dhcp6_srv.cc:268
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:804
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:410
std::vector< uint32_t > CfgMACSources
Container for defined MAC/hardware address sources.
const isc::log::MessageID DHCP6_RELEASE_NA_FAIL_WRONG_IAID
Defines the Dhcp6to4Ipc class.
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition: dhcp6_srv.h:1190
const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_DROP
const isc::log::MessageID DHCP6_DECLINE_LEASE
std::vector< Resource > HintContainer
Container for client&#39;s hints.
Definition: alloc_engine.h:185
const isc::log::MessageID DHCP6_BUFFER_RECEIVED
Definition: dhcp6.h:26
void addHint(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128, const uint32_t preferred=0, const uint32_t valid=0)
Convenience method adding new hint.
const int DBG_DHCP6_DETAIL
Debug level used to trace detailed errors.
Definition: dhcp6_log.h:51
const isc::log::MessageID DHCP6_PACKET_RECEIVE_FAIL
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition: lease.h:677
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:40
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const =0
Returns existing IPv6 lease for a given IPv6 address.
void addOption(OptionPtr opt)
Adds a sub-option.
Definition: option.cc:331
void processClientFqdn(const Pkt6Ptr &question, const Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Processes Client FQDN Option.
Definition: dhcp6_srv.cc:1997
const isc::log::MessageID DHCP6_HOOK_DDNS_UPDATE
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
bool empty() const
Check if classes is empty.
Definition: classify.h:138
Pkt6Ptr processRequest(AllocEngine::ClientContext6 &ctx)
Processes incoming Request and returns Reply response.
Definition: dhcp6_srv.cc:3464
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
void discardPackets()
Discards parked packets Clears the packet parking lots of all packets.
Definition: dhcp6_srv.cc:4562
Pkt6Ptr processRelease(AllocEngine::ClientContext6 &ctx)
Process incoming Release message.
Definition: dhcp6_srv.cc:3656
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
the lease contains non-temporary IPv6 address
Definition: lease.h:47
OptionPtr option_
Option instance.
Definition: cfg_option.h:49
const isc::log::MessageID DHCP6_PACKET_SEND_FAIL
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:4377
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:30
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
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
const isc::log::MessageID DHCP6_PROCESS_IA_PD_REQUEST
const isc::log::MessageID DHCP6_RELEASE_NA_DELETED
const isc::log::MessageID DHCP6_DDNS_FQDN_GENERATED
const isc::log::MessageID DHCP6_PACKET_SEND
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
boost::shared_ptr< Option6PDExclude > Option6PDExcludePtr
Pointer to the Option6PDExclude object.
Client race avoidance RAII handler.
bool fake_allocation_
Indicates if this is a real or fake allocation.
Definition: alloc_engine.h:233
uint16_t client_port_
UDP port number to which server sends all responses.
Definition: dhcp6_srv.h:1133
const isc::log::MessageID DHCP6_PROCESS_IA_PD_EXTEND
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-lfc.
boost::shared_ptr< Option6StatusCode > Option6StatusCodePtr
Pointer to the isc::dhcp::Option6StatusCode.
const isc::log::MessageID DHCP6_RELEASE_NA_FAIL_WRONG_DUID
Lease6Collection new_leases_
A collection of newly allocated leases.
Definition: alloc_engine.h:288
void initContext(const Pkt6Ptr &pkt, AllocEngine::ClientContext6 &ctx, bool &drop)
Initializes client context for specified packet.
Definition: dhcp6_srv.cc:494
const isc::log::MessageID DHCP6_RESPONSE_DATA
const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS2
const isc::log::MessageID DHCP6_HOOK_PACKET_RCVD_SKIP
const isc::log::MessageID DHCP6_HOOK_BUFFER_RCVD_SKIP
uint32_t iaid_
The IAID field from IA_NA or IA_PD that is being processed.
Definition: alloc_engine.h:297
const char *const config_report[]
const isc::log::MessageID DHCP6_PACKET_PROCESS_FAIL
void checkDynamicSubnetChange(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, const Subnet6Ptr orig_subnet)
Iterates over new leases, update stale DNS entries.
Definition: dhcp6_srv.cc:4605
const int DBG_DHCP6_DETAIL_DATA
This level is used to log the contents of packets received and sent.
Definition: dhcp6_log.h:54
D2ClientMgr isolates Kea from the details of being a D2 client.
Definition: d2_client_mgr.h:79
void run_one()
Main server processing step.
Definition: dhcp6_srv.cc:628
Lease::Type type_
Lease type (IA or PD)
Definition: alloc_engine.h:300
const isc::log::MessageID DHCP6_DDNS_REMOVE_OLD_LEASE_FQDN
std::string getDomainName() const
Returns the domain-name in the text format.
void getUpdateDirections(const T &fqdn_resp, bool &forward, bool &reverse)
Get directional update flags based on server FQDN flags.
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:3206
static const size_t MAX_DUID_LEN
maximum duid size As defined in RFC 8415, section 11.1
Definition: duid.h:31
const std::vector< T > & getValues() const
Return collection of option values.
const isc::log::MessageID DHCP6_HOOK_LEASES6_COMMITTED_DROP
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp6_srv.cc:4356
This exception is thrown when DHCP server hits the error which should result in discarding the messag...
Definition: dhcp6_srv.h:48
Pkt6Ptr query_
A pointer to the client&#39;s message.
Definition: alloc_engine.h:226
boost::shared_ptr< ClientClassDefList > ClientClassDefListPtr
Defines a pointer to a ClientClassDefList.
const isc::log::MessageID DHCP6_PD_LEASE_RENEW
Exception thrown when a call to select is interrupted by a signal.
Definition: iface_mgr.h:55
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
static const uint8_t FLAG_S
S bit.
OptionPtr assignIA_PD(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, boost::shared_ptr< Option6IA > ia)
Processes IA_PD option (and assigns prefixes if necessary).
Definition: dhcp6_srv.cc:2399
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition: hooks_log.h:37
void processDhcp6QueryAndSendResponse(Pkt6Ptr &query, Pkt6Ptr &rsp)
Process a single incoming DHCPv6 query.
Definition: dhcp6_srv.cc:907
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition: dhcp6_srv.h:110
CtrlAgentHooks Hooks
std::string generateFqdn(const asiolink::IOAddress &address, const DdnsParams &ddns_params, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire)
Definition: lease.h:34
Pkt6Ptr processRenew(AllocEngine::ClientContext6 &ctx)
Processes incoming Renew message.
Definition: dhcp6_srv.cc:3498
const isc::log::MessageID DHCP6_PD_LEASE_ADVERT_FAIL
void stopD2()
Stops DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp6_srv.cc:4368
#define DHCP6_OPTION_SPACE
void closeSockets()
Closes all open sockets.
Definition: iface_mgr.cc:287
bool rev_dns_update_
A boolean value which indicates that server takes responsibility for the reverse DNS Update for this ...
Definition: alloc_engine.h:273
const isc::log::MessageID DHCP6_DECLINE_FAIL
std::list< std::list< std::string > > jsonPathsToRedact() const final override
Return a list of all paths that contain passwords or secrets for kea-dhcp6.
Definition: dhcp6_srv.cc:4656
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
An exception that is thrown if a DHCPv6 protocol violation occurs while processing a message (e...
Definition: utils.h:17
Container class for handling the DHCID value within a NameChangeRequest.
Definition: ncr_msg.h:86
boost::shared_ptr< Lease6Collection > Lease6CollectionPtr
A shared pointer to the collection of IPv6 leases.
Definition: lease.h:680
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:276
isc::dhcp::Subnet6Ptr selectSubnet(const Pkt6Ptr &question, bool &drop)
Selects a subnet for a given client&#39;s packet.
Definition: dhcp6_srv.cc:1872
const_iterator cend() const
Iterators to the past the end element.
Definition: classify.h:165
const isc::log::MessageID DHCP6_PROCESS_IA_NA_REQUEST
bool tryLock(Pkt4Ptr query, ContinuationPtr cont=ContinuationPtr())
Tries to acquires a client.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
OptionPtr serverid_
Server DUID (to be sent in server-identifier option)
Definition: dhcp6_srv.h:1164
const isc::log::MessageID DHCP6_REQUIRED_OPTIONS_CHECK_FAIL
void processDhcp6Query(Pkt6Ptr &query, Pkt6Ptr &rsp)
Process a single incoming DHCPv6 query.
Definition: dhcp6_srv.cc:925
void createIAContext()
Creates new IA context.
Definition: alloc_engine.h:431
void setPacketStatisticsDefaults()
This function sets statistics related to DHCPv6 packets processing to their initial values...
Definition: dhcp6_srv.cc:258
IAContext & currentIA()
Returns IA specific context for the currently processed IA.
Definition: alloc_engine.h:420
boost::shared_ptr< const CfgHostOperations > ConstCfgHostOperationsPtr
Pointer to the const object.
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition: dhcp6_srv.h:1182
void close()
Close communication socket.
Definition: dhcp4o6_ipc.cc:118
uint32_t getIAID() const
Returns IA identifier.
Definition: option6_ia.h:87
const isc::log::MessageID DHCP6_DECLINE_FAIL_IAID_MISMATCH
const isc::log::MessageID DHCP6_DDNS_RESPONSE_FQDN_DATA
const isc::log::MessageID DHCP6_HOOK_LEASES6_COMMITTED_PARK
Pkt6Ptr processDecline(AllocEngine::ClientContext6 &ctx)
Process incoming Decline message.
Definition: dhcp6_srv.cc:3682
void suspendUpdates()
Suspends sending requests.
const isc::log::MessageID DHCP6_LEASE_ADVERT
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition: cfgmgr.cc:66
virtual bool deleteLease(const Lease4Ptr &lease)=0
Deletes an IPv4 lease.
void send(const Pkt6Ptr &pkt)
Send message over IPC.
Definition: dhcp4o6_ipc.cc:226
const isc::log::MessageID DHCP6_BUFFER_WAIT_SIGNAL
void processDhcp4Query(const Pkt6Ptr &dhcp4_query)
Processes incoming DHCPv4-query message.
Definition: dhcp6_srv.cc:4018
const isc::log::MessageID DHCP6_DECLINE_FAIL_DUID_MISMATCH
Read mutex RAII handler.
bool isClientClassBuiltIn(const ClientClass &client_class)
Check if a client class name is builtin.
const isc::log::MessageID DHCP6_HOOK_LEASES6_PARKING_LOT_FULL
Factory for generating DUIDs (DHCP Unique Identifiers).
Definition: duid_factory.h:63
void processPacketAndSendResponse(Pkt6Ptr &query)
Process a single incoming DHCPv6 packet and sends the response.
Definition: dhcp6_srv.cc:716
boost::shared_ptr< Subnet6 > Subnet6Ptr
A pointer to a Subnet6 object.
Definition: subnet.h:635
void insert(const ClientClass &class_name)
Insert an element.
Definition: classify.h:128
void setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr &subnet, Option6IAPtr &resp)
Sets the T1 and T2 timers in the outbound IA.
Definition: dhcp6_srv.cc:4569
void sendResponseNoThrow(hooks::CalloutHandlePtr &callout_handle, Pkt6Ptr &query, Pkt6Ptr &rsp)
Process an unparked DHCPv6 packet and sends the response.
Definition: dhcp6_srv.cc:1216
uint32_t getVendorId() const
Returns enterprise id.
IdentifierType
Type of the host identifier.
Definition: host.h:307
const isc::log::MessageID DHCP6_LEASE_ALLOC_FAIL
hooks::CalloutHandlePtr callout_handle_
Callout handle associated with the client&#39;s message.
Definition: alloc_engine.h:282
bool declineLeases(const Pkt6Ptr &decline, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to decline all leases in specified Decline message.
Definition: dhcp6_srv.cc:3712
static LeaseMgr & instance()
Return current lease manager.
const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS_EARLY
std::string qualifyName(const std::string &partial_name, const DdnsParams &ddns_params, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
Class that represents IAPREFIX option in DHCPv6.
isc::hooks::CalloutHandlePtr getCalloutHandle(const T &pktptr)
CalloutHandle Store.
const isc::log::MessageID EVAL_RESULT
Definition: eval_messages.h:55