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