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