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