Kea  2.3.5-git
dhcp4_srv.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2023 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 #include <kea_version.h>
9 
10 #include <dhcp/dhcp4.h>
11 #include <dhcp/duid.h>
12 #include <dhcp/hwaddr.h>
13 #include <dhcp/iface_mgr.h>
14 #include <dhcp/libdhcp++.h>
15 #include <dhcp/option4_addrlst.h>
16 #include <dhcp/option_custom.h>
17 #include <dhcp/option_int.h>
18 #include <dhcp/option_int_array.h>
19 #include <dhcp/option_vendor.h>
20 #include <dhcp/option_string.h>
21 #include <dhcp/pkt4.h>
22 #include <dhcp/pkt4o6.h>
23 #include <dhcp/pkt6.h>
25 #include <dhcp4/client_handler.h>
26 #include <dhcp4/dhcp4to6_ipc.h>
27 #include <dhcp4/dhcp4_log.h>
28 #include <dhcp4/dhcp4_srv.h>
30 #include <dhcpsrv/cfgmgr.h>
32 #include <dhcpsrv/cfg_iface.h>
34 #include <dhcpsrv/cfg_subnets4.h>
35 #include <dhcpsrv/fuzz.h>
36 #include <dhcpsrv/lease_mgr.h>
38 #include <dhcpsrv/ncr_generator.h>
39 #include <dhcpsrv/shared_network.h>
40 #include <dhcpsrv/subnet.h>
42 #include <dhcpsrv/utils.h>
43 #include <eval/evaluate.h>
44 #include <eval/eval_messages.h>
45 #include <hooks/callout_handle.h>
46 #include <hooks/hooks_log.h>
47 #include <hooks/hooks_manager.h>
48 #include <stats/stats_mgr.h>
49 #include <util/strutil.h>
50 #include <log/logger.h>
51 #include <cryptolink/cryptolink.h>
52 #include <cfgrpt/config_report.h>
53 
54 #ifdef HAVE_MYSQL
56 #endif
57 #ifdef HAVE_PGSQL
59 #endif
61 
62 #include <boost/algorithm/string.hpp>
63 #include <boost/foreach.hpp>
64 #include <boost/pointer_cast.hpp>
65 #include <boost/shared_ptr.hpp>
66 
67 #include <functional>
68 #include <iomanip>
69 #include <set>
70 #include <cstdlib>
71 
72 using namespace isc;
73 using namespace isc::asiolink;
74 using namespace isc::cryptolink;
75 using namespace isc::dhcp;
76 using namespace isc::dhcp_ddns;
77 using namespace isc::hooks;
78 using namespace isc::log;
79 using namespace isc::stats;
80 using namespace isc::util;
81 using namespace std;
82 namespace ph = std::placeholders;
83 
84 namespace {
85 
87 struct Dhcp4Hooks {
88  int hook_index_buffer4_receive_;
89  int hook_index_pkt4_receive_;
90  int hook_index_subnet4_select_;
91  int hook_index_leases4_committed_;
92  int hook_index_lease4_release_;
93  int hook_index_pkt4_send_;
94  int hook_index_buffer4_send_;
95  int hook_index_lease4_decline_;
96  int hook_index_host4_identifier_;
97  int hook_index_ddns4_update_;
98 
100  Dhcp4Hooks() {
101  hook_index_buffer4_receive_ = HooksManager::registerHook("buffer4_receive");
102  hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive");
103  hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
104  hook_index_leases4_committed_ = HooksManager::registerHook("leases4_committed");
105  hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
106  hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send");
107  hook_index_buffer4_send_ = HooksManager::registerHook("buffer4_send");
108  hook_index_lease4_decline_ = HooksManager::registerHook("lease4_decline");
109  hook_index_host4_identifier_ = HooksManager::registerHook("host4_identifier");
110  hook_index_ddns4_update_ = HooksManager::registerHook("ddns4_update");
111  }
112 };
113 
116 std::set<std::string> dhcp4_statistics = {
117  "pkt4-received",
118  "pkt4-discover-received",
119  "pkt4-offer-received",
120  "pkt4-request-received",
121  "pkt4-ack-received",
122  "pkt4-nak-received",
123  "pkt4-release-received",
124  "pkt4-decline-received",
125  "pkt4-inform-received",
126  "pkt4-unknown-received",
127  "pkt4-sent",
128  "pkt4-offer-sent",
129  "pkt4-ack-sent",
130  "pkt4-nak-sent",
131  "pkt4-parse-failed",
132  "pkt4-receive-drop",
133  "v4-allocation-fail",
134  "v4-allocation-fail-shared-network",
135  "v4-allocation-fail-subnet",
136  "v4-allocation-fail-no-pools",
137  "v4-allocation-fail-classes",
138  "v4-reservation-conflicts"
139 };
140 
141 } // end of anonymous namespace
142 
143 // Declare a Hooks object. As this is outside any function or method, it
144 // will be instantiated (and the constructor run) when the module is loaded.
145 // As a result, the hook indexes will be defined before any method in this
146 // module is called.
147 Dhcp4Hooks Hooks;
148 
149 namespace isc {
150 namespace dhcp {
151 
152 Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine,
153  const Pkt4Ptr& query,
155  const Subnet4Ptr& subnet,
156  bool& drop)
157  : alloc_engine_(alloc_engine), query_(query), resp_(),
158  context_(context) {
159 
160  if (!alloc_engine_) {
161  isc_throw(BadValue, "alloc_engine value must not be NULL"
162  " when creating an instance of the Dhcpv4Exchange");
163  }
164 
165  if (!query_) {
166  isc_throw(BadValue, "query value must not be NULL when"
167  " creating an instance of the Dhcpv4Exchange");
168  }
169 
170  // Reset the given context argument.
171  context.reset();
172 
173  // Create response message.
174  initResponse();
175  // Select subnet for the query message.
176  context_->subnet_ = subnet;
177 
178  // If subnet found, retrieve client identifier which will be needed
179  // for allocations and search for reservations associated with a
180  // subnet/shared network.
182  if (subnet && !context_->early_global_reservations_lookup_) {
183  OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
184  if (opt_clientid) {
185  context_->clientid_.reset(new ClientId(opt_clientid->getData()));
186  }
187  }
188 
189  if (subnet) {
190  // Find static reservations if not disabled for our subnet.
191  if (subnet->getReservationsInSubnet() ||
192  subnet->getReservationsGlobal()) {
193  // Before we can check for static reservations, we need to prepare a set
194  // of identifiers to be used for this.
195  if (!context_->early_global_reservations_lookup_) {
196  setHostIdentifiers(context_);
197  }
198 
199  // Check for static reservations.
200  alloc_engine->findReservation(*context_);
201 
202  // Get shared network to see if it is set for a subnet.
203  subnet->getSharedNetwork(sn);
204  }
205  }
206 
207  // Global host reservations are independent of a selected subnet. If the
208  // global reservations contain client classes we should use them in case
209  // they are meant to affect pool selection. Also, if the subnet does not
210  // belong to a shared network we can use the reserved client classes
211  // because there is no way our subnet could change. Such classes may
212  // affect selection of a pool within the selected subnet.
213  auto global_host = context_->globalHost();
214  auto current_host = context_->currentHost();
215  if ((!context_->early_global_reservations_lookup_ &&
216  global_host && !global_host->getClientClasses4().empty()) ||
217  (!sn && current_host && !current_host->getClientClasses4().empty())) {
218  // We have already evaluated client classes and some of them may
219  // be in conflict with the reserved classes. Suppose there are
220  // two classes defined in the server configuration: first_class
221  // and second_class and the test for the second_class it looks
222  // like this: "not member('first_class')". If the first_class
223  // initially evaluates to false, the second_class evaluates to
224  // true. If the first_class is now set within the hosts reservations
225  // and we don't remove the previously evaluated second_class we'd
226  // end up with both first_class and second_class evaluated to
227  // true. In order to avoid that, we have to remove the classes
228  // evaluated in the first pass and evaluate them again. As
229  // a result, the first_class set via the host reservation will
230  // replace the second_class because the second_class will this
231  // time evaluate to false as desired.
233  setReservedClientClasses(context_);
234  evaluateClasses(query, false);
235  }
236 
237  // Set KNOWN builtin class if something was found, UNKNOWN if not.
238  if (!context_->hosts_.empty()) {
239  query->addClass("KNOWN");
241  .arg(query->getLabel())
242  .arg("KNOWN");
243  } else {
244  query->addClass("UNKNOWN");
246  .arg(query->getLabel())
247  .arg("UNKNOWN");
248  }
249 
250  // Perform second pass of classification.
251  evaluateClasses(query, true);
252 
253  const ClientClasses& classes = query_->getClasses();
254  if (!classes.empty()) {
256  .arg(query_->getLabel())
257  .arg(classes.toText());
258  }
259 
260  // Check the DROP special class.
261  if (query_->inClass("DROP")) {
263  .arg(query_->toText());
264  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
265  static_cast<int64_t>(1));
266  drop = true;
267  }
268 }
269 
270 void
272  uint8_t resp_type = 0;
273  switch (getQuery()->getType()) {
274  case DHCPDISCOVER:
275  resp_type = DHCPOFFER;
276  break;
277  case DHCPREQUEST:
278  case DHCPINFORM:
279  resp_type = DHCPACK;
280  break;
281  default:
282  ;
283  }
284  // Only create a response if one is required.
285  if (resp_type > 0) {
286  resp_.reset(new Pkt4(resp_type, getQuery()->getTransid()));
287  copyDefaultFields();
288  copyDefaultOptions();
289 
290  if (getQuery()->isDhcp4o6()) {
291  initResponse4o6();
292  }
293  }
294 }
295 
296 void
298  Pkt4o6Ptr query = boost::dynamic_pointer_cast<Pkt4o6>(getQuery());
299  if (!query) {
300  return;
301  }
302  const Pkt6Ptr& query6 = query->getPkt6();
303  Pkt6Ptr resp6(new Pkt6(DHCPV6_DHCPV4_RESPONSE, query6->getTransid()));
304  // Don't add client-id or server-id
305  // But copy relay info
306  if (!query6->relay_info_.empty()) {
307  resp6->copyRelayInfo(query6);
308  }
309  // Copy interface, and remote address and port
310  resp6->setIface(query6->getIface());
311  resp6->setIndex(query6->getIndex());
312  resp6->setRemoteAddr(query6->getRemoteAddr());
313  resp6->setRemotePort(query6->getRemotePort());
314  resp_.reset(new Pkt4o6(resp_, resp6));
315 }
316 
317 void
318 Dhcpv4Exchange::copyDefaultFields() {
319  resp_->setIface(query_->getIface());
320  resp_->setIndex(query_->getIndex());
321 
322  // explicitly set this to 0
323  resp_->setSiaddr(IOAddress::IPV4_ZERO_ADDRESS());
324  // ciaddr is always 0, except for the Renew/Rebind state and for
325  // Inform when it may be set to the ciaddr sent by the client.
326  if (query_->getType() == DHCPINFORM) {
327  resp_->setCiaddr(query_->getCiaddr());
328  } else {
329  resp_->setCiaddr(IOAddress::IPV4_ZERO_ADDRESS());
330  }
331  resp_->setHops(query_->getHops());
332 
333  // copy MAC address
334  resp_->setHWAddr(query_->getHWAddr());
335 
336  // relay address
337  resp_->setGiaddr(query_->getGiaddr());
338 
339  // If src/dest HW addresses are used by the packet filtering class
340  // we need to copy them as well. There is a need to check that the
341  // address being set is not-NULL because an attempt to set the NULL
342  // HW would result in exception. If these values are not set, the
343  // the default HW addresses (zeroed) should be generated by the
344  // packet filtering class when creating Ethernet header for
345  // outgoing packet.
346  HWAddrPtr src_hw_addr = query_->getLocalHWAddr();
347  if (src_hw_addr) {
348  resp_->setLocalHWAddr(src_hw_addr);
349  }
350  HWAddrPtr dst_hw_addr = query_->getRemoteHWAddr();
351  if (dst_hw_addr) {
352  resp_->setRemoteHWAddr(dst_hw_addr);
353  }
354 
355  // Copy flags from the request to the response per RFC 2131
356  resp_->setFlags(query_->getFlags());
357 }
358 
359 void
360 Dhcpv4Exchange::copyDefaultOptions() {
361  // Let's copy client-id to response. See RFC6842.
362  // It is possible to disable RFC6842 to keep backward compatibility
363  bool echo = CfgMgr::instance().getCurrentCfg()->getEchoClientId();
364  OptionPtr client_id = query_->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
365  if (client_id && echo) {
366  resp_->addOption(client_id);
367  }
368 
369  // If this packet is relayed, we want to copy Relay Agent Info option
370  // when it is not empty.
371  OptionPtr rai = query_->getOption(DHO_DHCP_AGENT_OPTIONS);
372  if (rai && (rai->len() > Option::OPTION4_HDR_LEN)) {
373  resp_->addOption(rai);
374  }
375 
376  // RFC 3011 states about the Subnet Selection Option
377 
378  // "Servers configured to support this option MUST return an
379  // identical copy of the option to any client that sends it,
380  // regardless of whether or not the client requests the option in
381  // a parameter request list. Clients using this option MUST
382  // discard DHCPOFFER or DHCPACK packets that do not contain this
383  // option."
384  OptionPtr subnet_sel = query_->getOption(DHO_SUBNET_SELECTION);
385  if (subnet_sel) {
386  resp_->addOption(subnet_sel);
387  }
388 }
389 
390 void
392  const ConstCfgHostOperationsPtr cfg =
393  CfgMgr::instance().getCurrentCfg()->getCfgHostOperations4();
394 
395  // Collect host identifiers. The identifiers are stored in order of preference.
396  // The server will use them in that order to search for host reservations.
397  BOOST_FOREACH(const Host::IdentifierType& id_type,
398  cfg->getIdentifierTypes()) {
399  switch (id_type) {
400  case Host::IDENT_HWADDR:
401  if (context->hwaddr_ && !context->hwaddr_->hwaddr_.empty()) {
402  context->addHostIdentifier(id_type, context->hwaddr_->hwaddr_);
403  }
404  break;
405 
406  case Host::IDENT_DUID:
407  if (context->clientid_) {
408  const std::vector<uint8_t>& vec = context->clientid_->getDuid();
409  if (!vec.empty()) {
410  // Client identifier type = DUID? Client identifier holding a DUID
411  // comprises Type (1 byte), IAID (4 bytes), followed by the actual
412  // DUID. Thus, the minimal length is 6.
413  if ((vec[0] == CLIENT_ID_OPTION_TYPE_DUID) && (vec.size() > 5)) {
414  // Extract DUID, skip IAID.
415  context->addHostIdentifier(id_type,
416  std::vector<uint8_t>(vec.begin() + 5,
417  vec.end()));
418  }
419  }
420  }
421  break;
422 
424  {
425  OptionPtr rai = context->query_->getOption(DHO_DHCP_AGENT_OPTIONS);
426  if (rai) {
427  OptionPtr circuit_id_opt = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
428  if (circuit_id_opt) {
429  const OptionBuffer& circuit_id_vec = circuit_id_opt->getData();
430  if (!circuit_id_vec.empty()) {
431  context->addHostIdentifier(id_type, circuit_id_vec);
432  }
433  }
434  }
435  }
436  break;
437 
439  if (context->clientid_) {
440  const std::vector<uint8_t>& vec = context->clientid_->getDuid();
441  if (!vec.empty()) {
442  context->addHostIdentifier(id_type, vec);
443  }
444  }
445  break;
446  case Host::IDENT_FLEX:
447  {
448  if (!HooksManager::calloutsPresent(Hooks.hook_index_host4_identifier_)) {
449  break;
450  }
451 
452  CalloutHandlePtr callout_handle = getCalloutHandle(context->query_);
453 
455  std::vector<uint8_t> id;
456 
457  // Use the RAII wrapper to make sure that the callout handle state is
458  // reset when this object goes out of scope. All hook points must do
459  // it to prevent possible circular dependency between the callout
460  // handle and its arguments.
461  ScopedCalloutHandleState callout_handle_state(callout_handle);
462 
463  // Pass incoming packet as argument
464  callout_handle->setArgument("query4", context->query_);
465  callout_handle->setArgument("id_type", type);
466  callout_handle->setArgument("id_value", id);
467 
468  // Call callouts
469  HooksManager::callCallouts(Hooks.hook_index_host4_identifier_,
470  *callout_handle);
471 
472  callout_handle->getArgument("id_type", type);
473  callout_handle->getArgument("id_value", id);
474 
475  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_CONTINUE) &&
476  !id.empty()) {
477 
479  .arg(Host::getIdentifierAsText(type, &id[0], id.size()));
480 
481  context->addHostIdentifier(type, id);
482  }
483  break;
484  }
485  default:
486  ;
487  }
488  }
489 }
490 
491 void
493  const ClientClassDictionaryPtr& dict =
494  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
495  const ClientClassDefListPtr& defs_ptr = dict->getClasses();
496  for (auto def : *defs_ptr) {
497  // Only remove evaluated classes. Other classes can be
498  // assigned via hooks libraries and we should not remove
499  // them because there is no way they can be added back.
500  if (def->getMatchExpr()) {
501  query->classes_.erase(def->getName());
502  }
503  }
504 }
505 
506 void
508  if (context->currentHost() && context->query_) {
509  const ClientClasses& classes = context->currentHost()->getClientClasses4();
510  for (ClientClasses::const_iterator cclass = classes.cbegin();
511  cclass != classes.cend(); ++cclass) {
512  context->query_->addClass(*cclass);
513  }
514  }
515 }
516 
517 void
519  if (context_->subnet_) {
520  SharedNetwork4Ptr shared_network;
521  context_->subnet_->getSharedNetwork(shared_network);
522  if (shared_network) {
523  ConstHostPtr host = context_->currentHost();
524  if (host && (host->getIPv4SubnetID() != SUBNET_ID_GLOBAL)) {
525  setReservedClientClasses(context_);
526  }
527  }
528  }
529 }
530 
531 void
533  ConstHostPtr host = context_->currentHost();
534  // Nothing to do if host reservations not specified for this client.
535  if (host) {
536  if (!host->getNextServer().isV4Zero()) {
537  resp_->setSiaddr(host->getNextServer());
538  }
539 
540  std::string sname = host->getServerHostname();
541  if (!sname.empty()) {
542  resp_->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
543  sname.size());
544  }
545 
546  std::string bootfile = host->getBootFileName();
547  if (!bootfile.empty()) {
548  resp_->setFile(reinterpret_cast<const uint8_t*>(bootfile.c_str()),
549  bootfile.size());
550  }
551  }
552 }
553 
555  // Built-in vendor class processing
556  boost::shared_ptr<OptionString> vendor_class =
557  boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER));
558 
559  if (!vendor_class) {
560  return;
561  }
562 
563  pkt->addClass(Dhcpv4Srv::VENDOR_CLASS_PREFIX + vendor_class->getValue());
564 }
565 
567  // All packets belong to ALL.
568  pkt->addClass("ALL");
569 
570  // First: built-in vendor class processing.
571  classifyByVendor(pkt);
572 
573  // Run match expressions on classes not depending on KNOWN/UNKNOWN.
574  evaluateClasses(pkt, false);
575 }
576 
577 void Dhcpv4Exchange::evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known) {
578  // Note getClientClassDictionary() cannot be null
579  const ClientClassDictionaryPtr& dict =
580  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
581  const ClientClassDefListPtr& defs_ptr = dict->getClasses();
582  for (ClientClassDefList::const_iterator it = defs_ptr->cbegin();
583  it != defs_ptr->cend(); ++it) {
584  // Note second cannot be null
585  const ExpressionPtr& expr_ptr = (*it)->getMatchExpr();
586  // Nothing to do without an expression to evaluate
587  if (!expr_ptr) {
588  continue;
589  }
590  // Not the right time if only when required
591  if ((*it)->getRequired()) {
592  continue;
593  }
594  // Not the right pass.
595  if ((*it)->getDependOnKnown() != depend_on_known) {
596  continue;
597  }
598  (*it)->test(pkt, expr_ptr);
599  }
600 }
601 
602 const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
603 
604 Dhcpv4Srv::Dhcpv4Srv(uint16_t server_port, uint16_t client_port,
605  const bool use_bcast, const bool direct_response_desired)
606  : io_service_(new IOService()), server_port_(server_port),
607  client_port_(client_port), shutdown_(true),
608  alloc_engine_(), use_bcast_(use_bcast),
609  network_state_(new NetworkState(NetworkState::DHCPv4)),
610  cb_control_(new CBControlDHCPv4()),
611  test_send_responses_to_source_(false) {
612 
613  const char* env = std::getenv("KEA_TEST_SEND_RESPONSES_TO_SOURCE");
614  if (env) {
616  test_send_responses_to_source_ = true;
617  }
618 
620  .arg(server_port);
621 
622  try {
623  // Port 0 is used for testing purposes where we don't open broadcast
624  // capable sockets. So, set the packet filter handling direct traffic
625  // only if we are in non-test mode.
626  if (server_port) {
627  // First call to instance() will create IfaceMgr (it's a singleton)
628  // it may throw something if things go wrong.
629  // The 'true' value of the call to setMatchingPacketFilter imposes
630  // that IfaceMgr will try to use the mechanism to respond directly
631  // to the client which doesn't have address assigned. This capability
632  // may be lacking on some OSes, so there is no guarantee that server
633  // will be able to respond directly.
634  IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
635  }
636 
637  // Instantiate allocation engine. The number of allocation attempts equal
638  // to zero indicates that the allocation engine will use the number of
639  // attempts depending on the pool size.
640  alloc_engine_.reset(new AllocEngine(0));
641 
643 
644  } catch (const std::exception &e) {
646  shutdown_ = true;
647  return;
648  }
649 
650  // Initializing all observations with default value
652  shutdown_ = false;
653 }
654 
657 
658  // Iterate over set of observed statistics
659  for (auto it = dhcp4_statistics.begin(); it != dhcp4_statistics.end(); ++it) {
660  // Initialize them with default value 0
661  stats_mgr.setValue((*it), static_cast<int64_t>(0));
662  }
663 }
664 
666  // Discard any parked packets
667  discardPackets();
668 
669  try {
670  stopD2();
671  } catch (const std::exception& ex) {
672  // Highly unlikely, but lets Report it but go on
674  }
675 
676  try {
678  } catch (const std::exception& ex) {
679  // Highly unlikely, but lets Report it but go on
681  }
682 
684 
685  // The lease manager was instantiated during DHCPv4Srv configuration,
686  // so we should clean up after ourselves.
688 
689  // Explicitly unload hooks
690  HooksManager::prepareUnloadLibraries();
691  if (!HooksManager::unloadLibraries()) {
692  auto names = HooksManager::getLibraryNames();
693  std::string msg;
694  if (!names.empty()) {
695  msg = names[0];
696  for (size_t i = 1; i < names.size(); ++i) {
697  msg += std::string(", ") + names[i];
698  }
699  }
701  }
702 }
703 
704 void
707  shutdown_ = true;
708 }
709 
711 Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query, bool& drop,
712  bool sanity_only) const {
713 
714  // DHCPv4-over-DHCPv6 is a special (and complex) case
715  if (query->isDhcp4o6()) {
716  return (selectSubnet4o6(query, drop, sanity_only));
717  }
718 
719  Subnet4Ptr subnet;
720 
721  const SubnetSelector& selector = CfgSubnets4::initSelector(query);
722 
723  CfgMgr& cfgmgr = CfgMgr::instance();
724  subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet(selector);
725 
726  // Let's execute all callouts registered for subnet4_select
727  // (skip callouts if the selectSubnet was called to do sanity checks only)
728  if (!sanity_only &&
729  HooksManager::calloutsPresent(Hooks.hook_index_subnet4_select_)) {
730  CalloutHandlePtr callout_handle = getCalloutHandle(query);
731 
732  // Use the RAII wrapper to make sure that the callout handle state is
733  // reset when this object goes out of scope. All hook points must do
734  // it to prevent possible circular dependency between the callout
735  // handle and its arguments.
736  ScopedCalloutHandleState callout_handle_state(callout_handle);
737 
738  // Enable copying options from the packet within hook library.
739  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
740 
741  // Set new arguments
742  callout_handle->setArgument("query4", query);
743  callout_handle->setArgument("subnet4", subnet);
744  callout_handle->setArgument("subnet4collection",
745  cfgmgr.getCurrentCfg()->
746  getCfgSubnets4()->getAll());
747 
748  // Call user (and server-side) callouts
749  HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
750  *callout_handle);
751 
752  // Callouts decided to skip this step. This means that no subnet
753  // will be selected. Packet processing will continue, but it will
754  // be severely limited (i.e. only global options will be assigned)
755  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
758  .arg(query->getLabel());
759  return (Subnet4Ptr());
760  }
761 
762  // Callouts decided to drop the packet. It is a superset of the
763  // skip case so no subnet will be selected.
764  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
767  .arg(query->getLabel());
768  drop = true;
769  return (Subnet4Ptr());
770  }
771 
772  // Use whatever subnet was specified by the callout
773  callout_handle->getArgument("subnet4", subnet);
774  }
775 
776  if (subnet) {
777  // Log at higher debug level that subnet has been found.
779  .arg(query->getLabel())
780  .arg(subnet->getID());
781  // Log detailed information about the selected subnet at the
782  // lower debug level.
784  .arg(query->getLabel())
785  .arg(subnet->toText());
786 
787  } else {
790  .arg(query->getLabel());
791  }
792 
793  return (subnet);
794 }
795 
797 Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query, bool& drop,
798  bool sanity_only) const {
799  Subnet4Ptr subnet;
800 
801  SubnetSelector selector;
802  selector.ciaddr_ = query->getCiaddr();
803  selector.giaddr_ = query->getGiaddr();
804  selector.local_address_ = query->getLocalAddr();
805  selector.client_classes_ = query->classes_;
806  selector.iface_name_ = query->getIface();
807  // Mark it as DHCPv4-over-DHCPv6
808  selector.dhcp4o6_ = true;
809  // Now the DHCPv6 part
810  selector.remote_address_ = query->getRemoteAddr();
811  selector.first_relay_linkaddr_ = IOAddress("::");
812 
813  // Handle a DHCPv6 relayed query
814  Pkt4o6Ptr query4o6 = boost::dynamic_pointer_cast<Pkt4o6>(query);
815  if (!query4o6) {
816  isc_throw(Unexpected, "Can't get DHCP4o6 message");
817  }
818  const Pkt6Ptr& query6 = query4o6->getPkt6();
819 
820  // Initialize fields specific to relayed messages.
821  if (query6 && !query6->relay_info_.empty()) {
822  BOOST_REVERSE_FOREACH(Pkt6::RelayInfo relay, query6->relay_info_) {
823  if (!relay.linkaddr_.isV6Zero() &&
824  !relay.linkaddr_.isV6LinkLocal()) {
825  selector.first_relay_linkaddr_ = relay.linkaddr_;
826  break;
827  }
828  }
829  selector.interface_id_ =
830  query6->getAnyRelayOption(D6O_INTERFACE_ID,
832  }
833 
834  // If the Subnet Selection option is present, extract its value.
835  OptionPtr sbnsel = query->getOption(DHO_SUBNET_SELECTION);
836  if (sbnsel) {
837  OptionCustomPtr oc = boost::dynamic_pointer_cast<OptionCustom>(sbnsel);
838  if (oc) {
839  selector.option_select_ = oc->readAddress();
840  }
841  }
842 
843  CfgMgr& cfgmgr = CfgMgr::instance();
844  subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet4o6(selector);
845 
846  // Let's execute all callouts registered for subnet4_select.
847  // (skip callouts if the selectSubnet was called to do sanity checks only)
848  if (!sanity_only &&
849  HooksManager::calloutsPresent(Hooks.hook_index_subnet4_select_)) {
850  CalloutHandlePtr callout_handle = getCalloutHandle(query);
851 
852  // Use the RAII wrapper to make sure that the callout handle state is
853  // reset when this object goes out of scope. All hook points must do
854  // it to prevent possible circular dependency between the callout
855  // handle and its arguments.
856  ScopedCalloutHandleState callout_handle_state(callout_handle);
857 
858  // Set new arguments
859  callout_handle->setArgument("query4", query);
860  callout_handle->setArgument("subnet4", subnet);
861  callout_handle->setArgument("subnet4collection",
862  cfgmgr.getCurrentCfg()->
863  getCfgSubnets4()->getAll());
864 
865  // Call user (and server-side) callouts
866  HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
867  *callout_handle);
868 
869  // Callouts decided to skip this step. This means that no subnet
870  // will be selected. Packet processing will continue, but it will
871  // be severely limited (i.e. only global options will be assigned)
872  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
875  .arg(query->getLabel());
876  return (Subnet4Ptr());
877  }
878 
879  // Callouts decided to drop the packet. It is a superset of the
880  // skip case so no subnet will be selected.
881  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
884  .arg(query->getLabel());
885  drop = true;
886  return (Subnet4Ptr());
887  }
888 
889  // Use whatever subnet was specified by the callout
890  callout_handle->getArgument("subnet4", subnet);
891  }
892 
893  if (subnet) {
894  // Log at higher debug level that subnet has been found.
897  .arg(query->getLabel())
898  .arg(subnet->getID());
899  // Log detailed information about the selected subnet at the
900  // lower debug level.
903  .arg(query->getLabel())
904  .arg(subnet->toText());
905 
906  } else {
909  .arg(query->getLabel());
910  }
911 
912  return (subnet);
913 }
914 
915 Pkt4Ptr
917  return (IfaceMgr::instance().receive4(timeout));
918 }
919 
920 void
922  IfaceMgr::instance().send(packet);
923 }
924 
925 bool
928  // Pointer to client's query.
929  ctx->query_ = query;
930 
931  // Hardware address.
932  ctx->hwaddr_ = query->getHWAddr();
933 
934  // Get the early-global-reservations-lookup flag value.
937  if (egrl) {
938  ctx->early_global_reservations_lookup_ = egrl->boolValue();
939  }
940 
941  // Perform early global reservations lookup when wanted.
942  if (ctx->early_global_reservations_lookup_) {
943  // Retrieve retrieve client identifier.
944  OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
945  if (opt_clientid) {
946  ctx->clientid_.reset(new ClientId(opt_clientid->getData()));
947  }
948 
949  // Get the host identifiers.
951 
952  // Check for global host reservations.
953  ConstHostPtr global_host = alloc_engine_->findGlobalReservation(*ctx);
954 
955  if (global_host && !global_host->getClientClasses4().empty()) {
956  // Remove dependent evaluated classes.
958 
959  // Add classes from the global reservations.
960  const ClientClasses& classes = global_host->getClientClasses4();
961  for (ClientClasses::const_iterator cclass = classes.cbegin();
962  cclass != classes.cend(); ++cclass) {
963  query->addClass(*cclass);
964  }
965 
966  // Evaluate classes before KNOWN.
967  Dhcpv4Exchange::evaluateClasses(query, false);
968  }
969 
970  if (global_host) {
971  // Add the KNOWN class;
972  query->addClass("KNOWN");
974  .arg(query->getLabel())
975  .arg("KNOWN");
976 
977  // Evaluate classes after KNOWN.
978  Dhcpv4Exchange::evaluateClasses(query, true);
979 
980  // Check the DROP special class.
981  if (query->inClass("DROP")) {
984  .arg(query->toText());
985  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
986  static_cast<int64_t>(1));
987  return (false);
988  }
989 
990  // Store the reservation.
991  ctx->hosts_[SUBNET_ID_GLOBAL] = global_host;
992  }
993  }
994 
995  return (true);
996 }
997 
998 int
1000 #ifdef ENABLE_AFL
1001  // Set up structures needed for fuzzing.
1002  Fuzz fuzzer(4, server_port_);
1003  //
1004  // The next line is needed as a signature for AFL to recognize that we are
1005  // running persistent fuzzing. This has to be in the main image file.
1006  while (__AFL_LOOP(fuzzer.maxLoopCount())) {
1007  // Read from stdin and put the data read into an address/port on which
1008  // Kea is listening, read for Kea to read it via asynchronous I/O.
1009  fuzzer.transfer();
1010 #else
1011  while (!shutdown_) {
1012 #endif // ENABLE_AFL
1013  try {
1014  run_one();
1015  getIOService()->poll();
1016  } catch (const std::exception& e) {
1017  // General catch-all exception that are not caught by more specific
1018  // catches. This one is for exceptions derived from std::exception.
1020  .arg(e.what());
1021  } catch (...) {
1022  // General catch-all exception that are not caught by more specific
1023  // catches. This one is for other exceptions, not derived from
1024  // std::exception.
1026  }
1027  }
1028 
1029  // Stop everything before we change into single-threaded mode.
1031 
1032  // destroying the thread pool
1033  MultiThreadingMgr::instance().apply(false, 0, 0);
1034 
1035  return (getExitValue());
1036 }
1037 
1038 void
1040  // client's message and server's response
1041  Pkt4Ptr query;
1042 
1043  try {
1044  // Set select() timeout to 1s. This value should not be modified
1045  // because it is important that the select() returns control
1046  // frequently so as the IOService can be polled for ready handlers.
1047  uint32_t timeout = 1;
1048  query = receivePacket(timeout);
1049 
1050  // Log if packet has arrived. We can't log the detailed information
1051  // about the DHCP message because it hasn't been unpacked/parsed
1052  // yet, and it can't be parsed at this point because hooks will
1053  // have to process it first. The only information available at this
1054  // point are: the interface, source address and destination addresses
1055  // and ports.
1056  if (query) {
1058  .arg(query->getRemoteAddr().toText())
1059  .arg(query->getRemotePort())
1060  .arg(query->getLocalAddr().toText())
1061  .arg(query->getLocalPort())
1062  .arg(query->getIface());
1063  }
1064 
1065  // We used to log that the wait was interrupted, but this is no longer
1066  // the case. Our wait time is 1s now, so the lack of query packet more
1067  // likely means that nothing new appeared within a second, rather than
1068  // we were interrupted. And we don't want to print a message every
1069  // second.
1070 
1071  } catch (const SignalInterruptOnSelect&) {
1072  // Packet reception interrupted because a signal has been received.
1073  // This is not an error because we might have received a SIGTERM,
1074  // SIGINT, SIGHUP or SIGCHLD which are handled by the server. For
1075  // signals that are not handled by the server we rely on the default
1076  // behavior of the system.
1078  } catch (const std::exception& e) {
1079  // Log all other errors.
1081  }
1082 
1083  // Timeout may be reached or signal received, which breaks select()
1084  // with no reception occurred. No need to log anything here because
1085  // we have logged right after the call to receivePacket().
1086  if (!query) {
1087  return;
1088  }
1089 
1090  // If the DHCP service has been globally disabled, drop the packet.
1091  if (!network_state_->isServiceEnabled()) {
1093  .arg(query->getLabel());
1094  return;
1095  } else {
1096  if (MultiThreadingMgr::instance().getMode()) {
1097  typedef function<void()> CallBack;
1098  boost::shared_ptr<CallBack> call_back =
1099  boost::make_shared<CallBack>(std::bind(&Dhcpv4Srv::processPacketAndSendResponseNoThrow,
1100  this, query));
1101  if (!MultiThreadingMgr::instance().getThreadPool().add(call_back)) {
1103  }
1104  } else {
1106  }
1107  }
1108 }
1109 
1110 void
1112  try {
1114  } catch (const std::exception& e) {
1116  .arg(e.what());
1117  } catch (...) {
1119  }
1120 }
1121 
1122 void
1124  Pkt4Ptr rsp;
1125  processPacket(query, rsp);
1126  if (!rsp) {
1127  return;
1128  }
1129 
1130  CalloutHandlePtr callout_handle = getCalloutHandle(query);
1131  processPacketBufferSend(callout_handle, rsp);
1132 }
1133 
1134 void
1135 Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp, bool allow_packet_park) {
1136  // All packets belong to ALL.
1137  query->addClass("ALL");
1138 
1139  // Log reception of the packet. We need to increase it early, as any
1140  // failures in unpacking will cause the packet to be dropped. We
1141  // will increase type specific statistic further down the road.
1142  // See processStatsReceived().
1143  isc::stats::StatsMgr::instance().addValue("pkt4-received",
1144  static_cast<int64_t>(1));
1145 
1146  bool skip_unpack = false;
1147 
1148  // The packet has just been received so contains the uninterpreted wire
1149  // data; execute callouts registered for buffer4_receive.
1150  if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
1151  CalloutHandlePtr callout_handle = getCalloutHandle(query);
1152 
1153  // Use the RAII wrapper to make sure that the callout handle state is
1154  // reset when this object goes out of scope. All hook points must do
1155  // it to prevent possible circular dependency between the callout
1156  // handle and its arguments.
1157  ScopedCalloutHandleState callout_handle_state(callout_handle);
1158 
1159  // Enable copying options from the packet within hook library.
1160  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
1161 
1162  // Pass incoming packet as argument
1163  callout_handle->setArgument("query4", query);
1164 
1165  // Call callouts
1166  HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
1167  *callout_handle);
1168 
1169  // Callouts decided to drop the received packet.
1170  // The response (rsp) is null so the caller (run_one) will
1171  // immediately return too.
1172  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1175  .arg(query->getRemoteAddr().toText())
1176  .arg(query->getLocalAddr().toText())
1177  .arg(query->getIface());
1178  return;
1179  }
1180 
1181  // Callouts decided to skip the next processing step. The next
1182  // processing step would be to parse the packet, so skip at this
1183  // stage means that callouts did the parsing already, so server
1184  // should skip parsing.
1185  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
1188  .arg(query->getRemoteAddr().toText())
1189  .arg(query->getLocalAddr().toText())
1190  .arg(query->getIface());
1191  skip_unpack = true;
1192  }
1193 
1194  callout_handle->getArgument("query4", query);
1195  }
1196 
1197  // Unpack the packet information unless the buffer4_receive callouts
1198  // indicated they did it
1199  if (!skip_unpack) {
1200  try {
1202  .arg(query->getRemoteAddr().toText())
1203  .arg(query->getLocalAddr().toText())
1204  .arg(query->getIface());
1205  query->unpack();
1206  } catch (const SkipRemainingOptionsError& e) {
1207  // An option failed to unpack but we are to attempt to process it
1208  // anyway. Log it and let's hope for the best.
1211  .arg(e.what());
1212  } catch (const std::exception& e) {
1213  // Failed to parse the packet.
1215  .arg(query->getRemoteAddr().toText())
1216  .arg(query->getLocalAddr().toText())
1217  .arg(query->getIface())
1218  .arg(e.what());
1219 
1220  // Increase the statistics of parse failures and dropped packets.
1221  isc::stats::StatsMgr::instance().addValue("pkt4-parse-failed",
1222  static_cast<int64_t>(1));
1223  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
1224  static_cast<int64_t>(1));
1225  return;
1226  }
1227  }
1228 
1229  // Update statistics accordingly for received packet.
1230  processStatsReceived(query);
1231 
1232  // Assign this packet to one or more classes if needed. We need to do
1233  // this before calling accept(), because getSubnet4() may need client
1234  // class information.
1235  classifyPacket(query);
1236 
1237  // Now it is classified the deferred unpacking can be done.
1238  deferredUnpack(query);
1239 
1240  // Check whether the message should be further processed or discarded.
1241  // There is no need to log anything here. This function logs by itself.
1242  if (!accept(query)) {
1243  // Increase the statistic of dropped packets.
1244  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
1245  static_cast<int64_t>(1));
1246  return;
1247  }
1248 
1249  // We have sanity checked (in accept() that the Message Type option
1250  // exists, so we can safely get it here.
1251  int type = query->getType();
1253  .arg(query->getLabel())
1254  .arg(query->getName())
1255  .arg(type)
1256  .arg(query->getRemoteAddr())
1257  .arg(query->getLocalAddr())
1258  .arg(query->getIface());
1260  .arg(query->getLabel())
1261  .arg(query->toText());
1262 
1263  // Let's execute all callouts registered for pkt4_receive
1264  if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_receive_)) {
1265  CalloutHandlePtr callout_handle = getCalloutHandle(query);
1266 
1267  // Use the RAII wrapper to make sure that the callout handle state is
1268  // reset when this object goes out of scope. All hook points must do
1269  // it to prevent possible circular dependency between the callout
1270  // handle and its arguments.
1271  ScopedCalloutHandleState callout_handle_state(callout_handle);
1272 
1273  // Enable copying options from the packet within hook library.
1274  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
1275 
1276  // Pass incoming packet as argument
1277  callout_handle->setArgument("query4", query);
1278 
1279  // Call callouts
1280  HooksManager::callCallouts(Hooks.hook_index_pkt4_receive_,
1281  *callout_handle);
1282 
1283  // Callouts decided to skip the next processing step. The next
1284  // processing step would be to process the packet, so skip at this
1285  // stage means drop.
1286  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1287  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1290  .arg(query->getLabel());
1291  return;
1292  }
1293 
1294  callout_handle->getArgument("query4", query);
1295  }
1296 
1297  // Check the DROP special class.
1298  if (query->inClass("DROP")) {
1300  .arg(query->toText());
1301  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
1302  static_cast<int64_t>(1));
1303  return;
1304  }
1305 
1306  processDhcp4Query(query, rsp, allow_packet_park);
1307 }
1308 
1309 void
1311  bool allow_packet_park) {
1312  try {
1313  processDhcp4Query(query, rsp, allow_packet_park);
1314  if (!rsp) {
1315  return;
1316  }
1317 
1318  CalloutHandlePtr callout_handle = getCalloutHandle(query);
1319  processPacketBufferSend(callout_handle, rsp);
1320  } catch (const std::exception& e) {
1322  .arg(e.what());
1323  } catch (...) {
1325  }
1326 }
1327 
1328 void
1330  bool allow_packet_park) {
1331  // Create a client race avoidance RAII handler.
1332  ClientHandler client_handler;
1333 
1334  // Check for lease modifier queries from the same client being processed.
1335  if (MultiThreadingMgr::instance().getMode() &&
1336  ((query->getType() == DHCPDISCOVER) ||
1337  (query->getType() == DHCPREQUEST) ||
1338  (query->getType() == DHCPRELEASE) ||
1339  (query->getType() == DHCPDECLINE))) {
1340  ContinuationPtr cont =
1342  this, query, rsp, allow_packet_park));
1343  if (!client_handler.tryLock(query, cont)) {
1344  return;
1345  }
1346  }
1347 
1349  if (!earlyGHRLookup(query, ctx)) {
1350  return;
1351  }
1352 
1353  try {
1354  switch (query->getType()) {
1355  case DHCPDISCOVER:
1356  rsp = processDiscover(query, ctx);
1357  break;
1358 
1359  case DHCPREQUEST:
1360  // Note that REQUEST is used for many things in DHCPv4: for
1361  // requesting new leases, renewing existing ones and even
1362  // for rebinding.
1363  rsp = processRequest(query, ctx);
1364  break;
1365 
1366  case DHCPRELEASE:
1367  processRelease(query, ctx);
1368  break;
1369 
1370  case DHCPDECLINE:
1371  processDecline(query, ctx);
1372  break;
1373 
1374  case DHCPINFORM:
1375  rsp = processInform(query, ctx);
1376  break;
1377 
1378  default:
1379  // Only action is to output a message if debug is enabled,
1380  // and that is covered by the debug statement before the
1381  // "switch" statement.
1382  ;
1383  }
1384  } catch (const std::exception& e) {
1385 
1386  // Catch-all exception (we used to call only isc::Exception, but
1387  // std::exception could potentially be raised and if we don't catch
1388  // it here, it would be caught in main() and the process would
1389  // terminate). Just log the problem and ignore the packet.
1390  // (The problem is logged as a debug message because debug is
1391  // disabled by default - it prevents a DDOS attack based on the
1392  // sending of problem packets.)
1394  .arg(query->getLabel())
1395  .arg(e.what());
1396 
1397  // Increase the statistic of dropped packets.
1398  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
1399  static_cast<int64_t>(1));
1400  }
1401 
1402  CalloutHandlePtr callout_handle = getCalloutHandle(query);
1403  if (ctx && HooksManager::calloutsPresent(Hooks.hook_index_leases4_committed_)) {
1404  // The ScopedCalloutHandleState class which guarantees that the task
1405  // is added to the thread pool after the response is reset (if needed)
1406  // and CalloutHandle state is reset. In ST it does nothing.
1407  // A smart pointer is used to store the ScopedCalloutHandleState so that
1408  // a copy of the pointer is created by the lambda and only on the
1409  // destruction of the last reference the task is added.
1410  // In MT there are 2 cases:
1411  // 1. packet is unparked before current thread smart pointer to
1412  // ScopedCalloutHandleState is destroyed:
1413  // - the lambda uses the smart pointer to set the callout which adds the
1414  // task, but the task is added after ScopedCalloutHandleState is
1415  // destroyed, on the destruction of the last reference which is held
1416  // by the current thread.
1417  // 2. packet is unparked after the current thread smart pointer to
1418  // ScopedCalloutHandleState is destroyed:
1419  // - the current thread reference to ScopedCalloutHandleState is
1420  // destroyed, but the reference in the lambda keeps it alive until
1421  // the lambda is called and the last reference is released, at which
1422  // time the task is actually added.
1423  // Use the RAII wrapper to make sure that the callout handle state is
1424  // reset when this object goes out of scope. All hook points must do
1425  // it to prevent possible circular dependency between the callout
1426  // handle and its arguments.
1427  std::shared_ptr<ScopedCalloutHandleState> callout_handle_state =
1428  std::make_shared<ScopedCalloutHandleState>(callout_handle);
1429 
1430  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
1431 
1432  // Also pass the corresponding query packet as argument
1433  callout_handle->setArgument("query4", query);
1434 
1435  Lease4CollectionPtr new_leases(new Lease4Collection());
1436  // Filter out the new lease if it was reused so not committed.
1437  if (ctx->new_lease_ && (ctx->new_lease_->reuseable_valid_lft_ == 0)) {
1438  new_leases->push_back(ctx->new_lease_);
1439  }
1440  callout_handle->setArgument("leases4", new_leases);
1441 
1442  Lease4CollectionPtr deleted_leases(new Lease4Collection());
1443  if (ctx->old_lease_) {
1444  if ((!ctx->new_lease_) || (ctx->new_lease_->addr_ != ctx->old_lease_->addr_)) {
1445  deleted_leases->push_back(ctx->old_lease_);
1446  }
1447  }
1448  callout_handle->setArgument("deleted_leases4", deleted_leases);
1449 
1450  if (allow_packet_park) {
1451  // Get the parking limit. Parsing should ensure the value is present.
1452  uint32_t parked_packet_limit = 0;
1454  getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT);
1455  if (ppl) {
1456  parked_packet_limit = ppl->intValue();
1457  }
1458 
1459  if (parked_packet_limit) {
1460  const auto& parking_lot = ServerHooks::getServerHooks().
1461  getParkingLotPtr("leases4_committed");
1462 
1463  if (parking_lot && (parking_lot->size() >= parked_packet_limit)) {
1464  // We can't park it so we're going to throw it on the floor.
1467  .arg(parked_packet_limit)
1468  .arg(query->getLabel());
1469  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
1470  static_cast<int64_t>(1));
1471  rsp.reset();
1472  return;
1473  }
1474  }
1475 
1476  // We proactively park the packet. We'll unpark it without invoking
1477  // the callback (i.e. drop) unless the callout status is set to
1478  // NEXT_STEP_PARK. Otherwise the callback we bind here will be
1479  // executed when the hook library unparks the packet.
1480  HooksManager::park("leases4_committed", query,
1481  [this, callout_handle, query, rsp, callout_handle_state]() mutable {
1482  if (MultiThreadingMgr::instance().getMode()) {
1483  typedef function<void()> CallBack;
1484  boost::shared_ptr<CallBack> call_back =
1485  boost::make_shared<CallBack>(std::bind(&Dhcpv4Srv::sendResponseNoThrow,
1486  this, callout_handle, query, rsp));
1487  callout_handle_state->on_completion_ = [call_back]() {
1488  MultiThreadingMgr::instance().getThreadPool().add(call_back);
1489  };
1490  } else {
1491  processPacketPktSend(callout_handle, query, rsp);
1492  processPacketBufferSend(callout_handle, rsp);
1493  }
1494  });
1495  }
1496 
1497  try {
1498  // Call all installed callouts
1499  HooksManager::callCallouts(Hooks.hook_index_leases4_committed_,
1500  *callout_handle);
1501  } catch (...) {
1502  // Make sure we don't orphan a parked packet.
1503  if (allow_packet_park) {
1504  HooksManager::drop("leases4_committed", query);
1505  }
1506 
1507  throw;
1508  }
1509 
1510  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK)
1511  && allow_packet_park) {
1513  .arg(query->getLabel());
1514  // Since the hook library(ies) are going to do the unparking, then
1515  // reset the pointer to the response to indicate to the caller that
1516  // it should return, as the packet processing will continue via
1517  // the callback.
1518  rsp.reset();
1519  } else {
1520  // Drop the park job on the packet, it isn't needed.
1521  HooksManager::drop("leases4_committed", query);
1522  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1524  .arg(query->getLabel());
1525  rsp.reset();
1526  }
1527  }
1528  }
1529 
1530  // If we have a response prep it for shipment.
1531  if (rsp) {
1532  processPacketPktSend(callout_handle, query, rsp);
1533  }
1534 }
1535 
1536 void
1538  Pkt4Ptr& query, Pkt4Ptr& rsp) {
1539  try {
1540  processPacketPktSend(callout_handle, query, rsp);
1541  processPacketBufferSend(callout_handle, rsp);
1542  } catch (const std::exception& e) {
1544  .arg(e.what());
1545  } catch (...) {
1547  }
1548 }
1549 
1550 void
1552  Pkt4Ptr& query, Pkt4Ptr& rsp) {
1553  if (!rsp) {
1554  return;
1555  }
1556 
1557  // Specifies if server should do the packing
1558  bool skip_pack = false;
1559 
1560  // Execute all callouts registered for pkt4_send
1561  if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_send_)) {
1562 
1563  // Use the RAII wrapper to make sure that the callout handle state is
1564  // reset when this object goes out of scope. All hook points must do
1565  // it to prevent possible circular dependency between the callout
1566  // handle and its arguments.
1567  ScopedCalloutHandleState callout_handle_state(callout_handle);
1568 
1569  // Enable copying options from the query and response packets within
1570  // hook library.
1571  ScopedEnableOptionsCopy<Pkt4> query_resp_options_copy(query, rsp);
1572 
1573  // Pass incoming packet as argument
1574  callout_handle->setArgument("query4", query);
1575 
1576  // Set our response
1577  callout_handle->setArgument("response4", rsp);
1578 
1579  // Call all installed callouts
1580  HooksManager::callCallouts(Hooks.hook_index_pkt4_send_,
1581  *callout_handle);
1582 
1583  // Callouts decided to skip the next processing step. The next
1584  // processing step would be to pack the packet (create wire data).
1585  // That step will be skipped if any callout sets skip flag.
1586  // It essentially means that the callout already did packing,
1587  // so the server does not have to do it again.
1588  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
1590  .arg(query->getLabel());
1591  skip_pack = true;
1592  }
1593 
1595  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1597  .arg(rsp->getLabel());
1598  rsp.reset();
1599  return;
1600  }
1601  }
1602 
1603  if (!skip_pack) {
1604  try {
1606  .arg(rsp->getLabel());
1607  rsp->pack();
1608  } catch (const std::exception& e) {
1610  .arg(rsp->getLabel())
1611  .arg(e.what());
1612  }
1613  }
1614 }
1615 
1616 void
1618  Pkt4Ptr& rsp) {
1619  if (!rsp) {
1620  return;
1621  }
1622 
1623  try {
1624  // Now all fields and options are constructed into output wire buffer.
1625  // Option objects modification does not make sense anymore. Hooks
1626  // can only manipulate wire buffer at this stage.
1627  // Let's execute all callouts registered for buffer4_send
1628  if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
1629 
1630  // Use the RAII wrapper to make sure that the callout handle state is
1631  // reset when this object goes out of scope. All hook points must do
1632  // it to prevent possible circular dependency between the callout
1633  // handle and its arguments.
1634  ScopedCalloutHandleState callout_handle_state(callout_handle);
1635 
1636  // Enable copying options from the packet within hook library.
1637  ScopedEnableOptionsCopy<Pkt4> resp4_options_copy(rsp);
1638 
1639  // Pass incoming packet as argument
1640  callout_handle->setArgument("response4", rsp);
1641 
1642  // Call callouts
1643  HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
1644  *callout_handle);
1645 
1646  // Callouts decided to skip the next processing step. The next
1647  // processing step would be to parse the packet, so skip at this
1648  // stage means drop.
1649  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1650  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1653  .arg(rsp->getLabel());
1654  return;
1655  }
1656 
1657  callout_handle->getArgument("response4", rsp);
1658  }
1659 
1661  .arg(rsp->getLabel())
1662  .arg(rsp->getName())
1663  .arg(static_cast<int>(rsp->getType()))
1664  .arg(rsp->getLocalAddr().isV4Zero() ? "*" : rsp->getLocalAddr().toText())
1665  .arg(rsp->getLocalPort())
1666  .arg(rsp->getRemoteAddr())
1667  .arg(rsp->getRemotePort())
1668  .arg(rsp->getIface().empty() ? "to be determined from routing" :
1669  rsp->getIface());
1670 
1673  .arg(rsp->getLabel())
1674  .arg(rsp->getName())
1675  .arg(static_cast<int>(rsp->getType()))
1676  .arg(rsp->toText());
1677  sendPacket(rsp);
1678 
1679  // Update statistics accordingly for sent packet.
1680  processStatsSent(rsp);
1681 
1682  } catch (const std::exception& e) {
1684  .arg(rsp->getLabel())
1685  .arg(e.what());
1686  }
1687 }
1688 
1689 string
1691  if (!srvid) {
1692  isc_throw(BadValue, "NULL pointer passed to srvidToString()");
1693  }
1694  boost::shared_ptr<Option4AddrLst> generated =
1695  boost::dynamic_pointer_cast<Option4AddrLst>(srvid);
1696  if (!srvid) {
1697  isc_throw(BadValue, "Pointer to invalid option passed to srvidToString()");
1698  }
1699 
1700  Option4AddrLst::AddressContainer addrs = generated->getAddresses();
1701  if (addrs.size() != 1) {
1702  isc_throw(BadValue, "Malformed option passed to srvidToString(). "
1703  << "Expected to contain a single IPv4 address.");
1704  }
1705 
1706  return (addrs[0].toText());
1707 }
1708 
1709 void
1711 
1712  // Do not append generated server identifier if there is one appended already.
1713  // This is when explicitly configured server identifier option is present.
1714  if (ex.getResponse()->getOption(DHO_DHCP_SERVER_IDENTIFIER)) {
1715  return;
1716  }
1717 
1718  // Use local address on which the packet has been received as a
1719  // server identifier. In some cases it may be a different address,
1720  // e.g. broadcast packet or DHCPv4o6 packet.
1721  IOAddress local_addr = ex.getQuery()->getLocalAddr();
1722  Pkt4Ptr query = ex.getQuery();
1723 
1724  if (local_addr.isV4Bcast() || query->isDhcp4o6()) {
1725  local_addr = IfaceMgr::instance().getSocket(query).addr_;
1726  }
1727 
1729  local_addr));
1730  ex.getResponse()->addOption(opt_srvid);
1731 }
1732 
1733 void
1735  CfgOptionList& co_list = ex.getCfgOptionList();
1736 
1737  // Retrieve subnet.
1738  Subnet4Ptr subnet = ex.getContext()->subnet_;
1739  if (!subnet) {
1740  // All methods using the CfgOptionList object return soon when
1741  // there is no subnet so do the same
1742  return;
1743  }
1744 
1745  // Firstly, host specific options.
1746  const ConstHostPtr& host = ex.getContext()->currentHost();
1747  if (host && !host->getCfgOption4()->empty()) {
1748  co_list.push_back(host->getCfgOption4());
1749  }
1750 
1751  // Secondly, pool specific options.
1752  Pkt4Ptr resp = ex.getResponse();
1754  if (resp) {
1755  addr = resp->getYiaddr();
1756  }
1757  if (!addr.isV4Zero()) {
1758  PoolPtr pool = subnet->getPool(Lease::TYPE_V4, addr, false);
1759  if (pool && !pool->getCfgOption()->empty()) {
1760  co_list.push_back(pool->getCfgOption());
1761  }
1762  }
1763 
1764  // Thirdly, subnet configured options.
1765  if (!subnet->getCfgOption()->empty()) {
1766  co_list.push_back(subnet->getCfgOption());
1767  }
1768 
1769  // Fourthly, shared network specific options.
1770  SharedNetwork4Ptr network;
1771  subnet->getSharedNetwork(network);
1772  if (network && !network->getCfgOption()->empty()) {
1773  co_list.push_back(network->getCfgOption());
1774  }
1775 
1776  // Each class in the incoming packet
1777  const ClientClasses& classes = ex.getQuery()->getClasses();
1778  for (ClientClasses::const_iterator cclass = classes.cbegin();
1779  cclass != classes.cend(); ++cclass) {
1780  // Find the client class definition for this class
1781  const ClientClassDefPtr& ccdef = CfgMgr::instance().getCurrentCfg()->
1782  getClientClassDictionary()->findClass(*cclass);
1783  if (!ccdef) {
1784  // Not found: the class is built-in or not configured
1785  if (!isClientClassBuiltIn(*cclass)) {
1787  .arg(ex.getQuery()->getLabel())
1788  .arg(*cclass);
1789  }
1790  // Skip it
1791  continue;
1792  }
1793 
1794  if (ccdef->getCfgOption()->empty()) {
1795  // Skip classes which don't configure options
1796  continue;
1797  }
1798 
1799  co_list.push_back(ccdef->getCfgOption());
1800  }
1801 
1802  // Last global options
1803  if (!CfgMgr::instance().getCurrentCfg()->getCfgOption()->empty()) {
1804  co_list.push_back(CfgMgr::instance().getCurrentCfg()->getCfgOption());
1805  }
1806 }
1807 
1808 void
1810  // Get the subnet relevant for the client. We will need it
1811  // to get the options associated with it.
1812  Subnet4Ptr subnet = ex.getContext()->subnet_;
1813  // If we can't find the subnet for the client there is no way
1814  // to get the options to be sent to a client. We don't log an
1815  // error because it will be logged by the assignLease method
1816  // anyway.
1817  if (!subnet) {
1818  return;
1819  }
1820 
1821  // Unlikely short cut
1822  const CfgOptionList& co_list = ex.getCfgOptionList();
1823  if (co_list.empty()) {
1824  return;
1825  }
1826 
1827  Pkt4Ptr query = ex.getQuery();
1828  Pkt4Ptr resp = ex.getResponse();
1829  set<uint8_t> requested_opts;
1830 
1831  // try to get the 'Parameter Request List' option which holds the
1832  // codes of requested options.
1833  OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast<
1835 
1836  // Get the list of options that client requested.
1837  if (option_prl) {
1838  for (uint16_t code : option_prl->getValues()) {
1839  static_cast<void>(requested_opts.insert(code));
1840  }
1841  }
1842 
1843  // Iterate on the configured option list to add persistent options
1844  for (auto const& copts : co_list) {
1845  const OptionContainerPtr& opts = copts->getAll(DHCP4_OPTION_SPACE);
1846  if (!opts) {
1847  continue;
1848  }
1849  // Get persistent options
1850  const OptionContainerPersistIndex& idx = opts->get<2>();
1851  const OptionContainerPersistRange& range = idx.equal_range(true);
1852  for (OptionContainerPersistIndex::const_iterator desc = range.first;
1853  desc != range.second; ++desc) {
1854  // Add the persistent option code to requested options
1855  if (desc->option_) {
1856  static_cast<void>(requested_opts.insert(desc->option_->getType()));
1857  }
1858  }
1859  }
1860 
1861  // For each requested option code get the instance of the option
1862  // to be returned to the client.
1863  for (auto const& opt : requested_opts) {
1864  // Add nothing when it is already there.
1865  // Skip special cases: DHO_VIVSO_SUBOPTIONS
1866  if (opt == DHO_VIVSO_SUBOPTIONS) {
1867  continue;
1868  }
1869  if (!resp->getOption(opt)) {
1870  // Iterate on the configured option list
1871  for (auto const& copts : co_list) {
1872  OptionDescriptor desc = copts->get(DHCP4_OPTION_SPACE, opt);
1873  // Got it: add it and jump to the outer loop
1874  if (desc.option_) {
1875  resp->addOption(desc.option_);
1876  break;
1877  }
1878  }
1879  }
1880  }
1881 
1882  if (requested_opts.count(DHO_VIVSO_SUBOPTIONS) > 0) {
1883  set<uint32_t> vendor_ids;
1884  for (auto opt : resp->getOptions(DHO_VIVSO_SUBOPTIONS)) {
1885  OptionVendorPtr vendor_opts;
1886  vendor_opts = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1887  if (vendor_opts) {
1888  static_cast<void>(vendor_ids.insert(vendor_opts->getVendorId()));
1889  }
1890  }
1891  // Iterate on the configured option list
1892  for (auto const& copts : co_list) {
1893  for (OptionDescriptor desc : copts->getList(DHCP4_OPTION_SPACE,
1895  if (!desc.option_) {
1896  continue;
1897  }
1898  OptionVendorPtr vendor_opts =
1899  boost::dynamic_pointer_cast<OptionVendor>(desc.option_);
1900  if (!vendor_opts) {
1901  continue;
1902  }
1903  // Is the vendor id already in the response?
1904  uint32_t vendor_id = vendor_opts->getVendorId();
1905  if (vendor_ids.count(vendor_id) > 0) {
1906  continue;
1907  }
1908  // Got it: add it.
1909  resp->Pkt::addOption(desc.option_);
1910  static_cast<void>(vendor_ids.insert(vendor_id));
1911  }
1912  }
1913  }
1914 }
1915 
1916 void
1918  // Get the configured subnet suitable for the incoming packet.
1919  Subnet4Ptr subnet = ex.getContext()->subnet_;
1920 
1921  const CfgOptionList& co_list = ex.getCfgOptionList();
1922  // Leave if there is no subnet matching the incoming packet.
1923  // There is no need to log the error message here because
1924  // it will be logged in the assignLease() when it fails to
1925  // pick the suitable subnet. We don't want to duplicate
1926  // error messages in such case.
1927  //
1928  // Also, if there's no options to possibly assign, give up.
1929  if (!subnet || co_list.empty()) {
1930  return;
1931  }
1932 
1933  Pkt4Ptr query = ex.getQuery();
1934  Pkt4Ptr resp = ex.getResponse();
1935  set<uint32_t> vendor_ids;
1936 
1937  // The server could have provided the option using client classification or
1938  // hooks. If there're vendor info options in the response already, use them.
1939  map<uint32_t, OptionVendorPtr> vendor_rsps;
1940  for (auto opt : resp->getOptions(DHO_VIVSO_SUBOPTIONS)) {
1941  OptionVendorPtr vendor_rsp;
1942  vendor_rsp = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1943  if (vendor_rsp) {
1944  uint32_t vendor_id = vendor_rsp->getVendorId();
1945  vendor_rsps[vendor_id] = vendor_rsp;
1946  static_cast<void>(vendor_ids.insert(vendor_id));
1947  }
1948  }
1949 
1950  // Next, try to get the vendor-id from the client packet's
1951  // vendor-specific information option (125).
1952  map<uint32_t, OptionVendorPtr> vendor_reqs;
1953  for (auto opt : query->getOptions(DHO_VIVSO_SUBOPTIONS)) {
1954  OptionVendorPtr vendor_req;
1955  vendor_req = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1956  if (vendor_req) {
1957  uint32_t vendor_id = vendor_req->getVendorId();
1958  vendor_reqs[vendor_id] = vendor_req;
1959  static_cast<void>(vendor_ids.insert(vendor_id));
1960  }
1961  }
1962 
1963  // If there's no vendor option in either request or response, then there's no way
1964  // to figure out what the vendor-id values are and we give up.
1965  if (vendor_ids.empty()) {
1966  return;
1967  }
1968 
1969  map<uint32_t, set<uint8_t> > requested_opts;
1970 
1971  // Let's try to get ORO within that vendor-option.
1972  // This is specific to vendor-id=4491 (Cable Labs). Other vendors may have
1973  // different policies.
1974  OptionUint8ArrayPtr oro;
1975  if (vendor_reqs.count(VENDOR_ID_CABLE_LABS) > 0) {
1976  OptionVendorPtr vendor_req = vendor_reqs[VENDOR_ID_CABLE_LABS];
1977  OptionPtr oro_generic = vendor_req->getOption(DOCSIS3_V4_ORO);
1978  if (oro_generic) {
1979  // Vendor ID 4491 makes Kea look at DOCSIS3_V4_OPTION_DEFINITIONS
1980  // when parsing options. Based on that, oro_generic will have been
1981  // created as an OptionUint8Array, but might not be for other
1982  // vendor IDs.
1983  oro = boost::dynamic_pointer_cast<OptionUint8Array>(oro_generic);
1984  }
1985  if (oro) {
1986  set<uint8_t> oro_req_opts;
1987  for (uint8_t code : oro->getValues()) {
1988  static_cast<void>(oro_req_opts.insert(code));
1989  }
1990  requested_opts[VENDOR_ID_CABLE_LABS] = oro_req_opts;
1991  }
1992  }
1993 
1994  // Iterate on the configured option list to add persistent options
1995  for (uint32_t vendor_id : vendor_ids) {
1996  for (auto const& copts : co_list) {
1997  const OptionContainerPtr& opts = copts->getAll(vendor_id);
1998  if (!opts) {
1999  continue;
2000  }
2001  // Get persistent options
2002  const OptionContainerPersistIndex& idx = opts->get<2>();
2003  const OptionContainerPersistRange& range = idx.equal_range(true);
2004  for (OptionContainerPersistIndex::const_iterator desc = range.first;
2005  desc != range.second; ++desc) {
2006  if (!desc->option_) {
2007  continue;
2008  }
2009  // Add the persistent option code to requested options
2010  static_cast<void>(requested_opts[vendor_id].insert(desc->option_->getType()));
2011  }
2012  }
2013 
2014  // If there is nothing to add don't do anything with this vendor.
2015  // This will explicitly not echo back vendor options from the request
2016  // that either correspond to a vendor not known to Kea even if the
2017  // option encapsulates data or there are no persistent options
2018  // configured for this vendor so Kea does not send any option back.
2019  if (requested_opts[vendor_id].empty()) {
2020  continue;
2021  }
2022 
2023  // It's possible that the vendor opts option was inserted already
2024  // by client class or a hook. If that is so, let's use it.
2025  OptionVendorPtr vendor_rsp;
2026  if (vendor_rsps.count(vendor_id) > 0) {
2027  vendor_rsp = vendor_rsps[vendor_id];
2028  } else {
2029  vendor_rsp.reset(new OptionVendor(Option::V4, vendor_id));
2030  }
2031 
2032  // Get the list of options that client requested.
2033  bool added = false;
2034 
2035  for (uint8_t opt : requested_opts[vendor_id]) {
2036  if (!vendor_rsp->getOption(opt)) {
2037  for (auto const& copts : co_list) {
2038  OptionDescriptor desc = copts->get(vendor_id, opt);
2039  if (desc.option_) {
2040  vendor_rsp->addOption(desc.option_);
2041  added = true;
2042  break;
2043  }
2044  }
2045  }
2046  }
2047 
2048  // If we added some sub-options and the vendor opts option is not in
2049  // the response already, then add it.
2050  if (added && (vendor_rsps.count(vendor_id) == 0)) {
2051  resp->Pkt::addOption(vendor_rsp);
2052  }
2053  }
2054 }
2055 
2056 void
2058  // Identify options that we always want to send to the
2059  // client (if they are configured).
2060  static const std::vector<uint16_t> required_options = {
2061  DHO_ROUTERS,
2065 
2066  // Get the subnet.
2067  Subnet4Ptr subnet = ex.getContext()->subnet_;
2068  if (!subnet) {
2069  return;
2070  }
2071 
2072  // Unlikely short cut
2073  const CfgOptionList& co_list = ex.getCfgOptionList();
2074  if (co_list.empty()) {
2075  return;
2076  }
2077 
2078  Pkt4Ptr resp = ex.getResponse();
2079 
2080  // Try to find all 'required' options in the outgoing
2081  // message. Those that are not present will be added.
2082  for (auto const& required : required_options) {
2083  OptionPtr opt = resp->getOption(required);
2084  if (!opt) {
2085  // Check whether option has been configured.
2086  for (auto const& copts : co_list) {
2087  OptionDescriptor desc = copts->get(DHCP4_OPTION_SPACE, required);
2088  if (desc.option_) {
2089  resp->addOption(desc.option_);
2090  break;
2091  }
2092  }
2093  }
2094  }
2095 }
2096 
2097 void
2099  // It is possible that client has sent both Client FQDN and Hostname
2100  // option. In that the server should prefer Client FQDN option and
2101  // ignore the Hostname option.
2102  try {
2103  Pkt4Ptr query = ex.getQuery();
2104  Pkt4Ptr resp = ex.getResponse();
2105  Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn>
2106  (query->getOption(DHO_FQDN));
2107  if (fqdn) {
2109  .arg(query->getLabel());
2110  processClientFqdnOption(ex);
2111 
2112  } else {
2115  .arg(query->getLabel());
2116  processHostnameOption(ex);
2117  }
2118 
2119  // Based on the output option added to the response above, we figure out
2120  // the values for the hostname and dns flags to set in the context. These
2121  // will be used to populate the lease.
2122  std::string hostname;
2123  bool fqdn_fwd = false;
2124  bool fqdn_rev = false;
2125 
2126 
2127  OptionStringPtr opt_hostname;
2128  fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn>(resp->getOption(DHO_FQDN));
2129  if (fqdn) {
2130  hostname = fqdn->getDomainName();
2131  CfgMgr::instance().getD2ClientMgr().getUpdateDirections(*fqdn, fqdn_fwd, fqdn_rev);
2132  } else {
2133  opt_hostname = boost::dynamic_pointer_cast<OptionString>
2134  (resp->getOption(DHO_HOST_NAME));
2135 
2136  if (opt_hostname) {
2137  hostname = opt_hostname->getValue();
2138  // DHO_HOST_NAME is string option which cannot be blank,
2139  // we use "." to know we should replace it with a fully
2140  // generated name. The local string variable needs to be
2141  // blank in logic below.
2142  if (hostname == ".") {
2143  hostname = "";
2144  }
2145 
2148  if (ex.getContext()->getDdnsParams()->getEnableUpdates()) {
2149  fqdn_fwd = true;
2150  fqdn_rev = true;
2151  }
2152  }
2153  }
2154 
2155  // Optionally, call a hook that may possibly override the decisions made
2156  // earlier.
2157  if (HooksManager::calloutsPresent(Hooks.hook_index_ddns4_update_)) {
2158  CalloutHandlePtr callout_handle = getCalloutHandle(query);
2159 
2160  // Use the RAII wrapper to make sure that the callout handle state is
2161  // reset when this object goes out of scope. All hook points must do
2162  // it to prevent possible circular dependency between the callout
2163  // handle and its arguments.
2164  ScopedCalloutHandleState callout_handle_state(callout_handle);
2165 
2166  // Setup the callout arguments.
2167  Subnet4Ptr subnet = ex.getContext()->subnet_;
2168  callout_handle->setArgument("query4", query);
2169  callout_handle->setArgument("response4", resp);
2170  callout_handle->setArgument("subnet4", subnet);
2171  callout_handle->setArgument("hostname", hostname);
2172  callout_handle->setArgument("fwd-update", fqdn_fwd);
2173  callout_handle->setArgument("rev-update", fqdn_rev);
2174  callout_handle->setArgument("ddns-params", ex.getContext()->getDdnsParams());
2175 
2176  // Call callouts
2177  HooksManager::callCallouts(Hooks.hook_index_ddns4_update_, *callout_handle);
2178 
2179  // Let's get the parameters returned by hook.
2180  string hook_hostname;
2181  bool hook_fqdn_fwd = false;
2182  bool hook_fqdn_rev = false;
2183  callout_handle->getArgument("hostname", hook_hostname);
2184  callout_handle->getArgument("fwd-update", hook_fqdn_fwd);
2185  callout_handle->getArgument("rev-update", hook_fqdn_rev);
2186 
2187  // If there's anything changed by the hook, log it and then update
2188  // the parameters.
2189  if ((hostname != hook_hostname) || (fqdn_fwd != hook_fqdn_fwd) ||
2190  (fqdn_rev != hook_fqdn_rev)) {
2192  .arg(hostname).arg(hook_hostname).arg(fqdn_fwd).arg(hook_fqdn_fwd)
2193  .arg(fqdn_rev).arg(hook_fqdn_rev);
2194  hostname = hook_hostname;
2195  fqdn_fwd = hook_fqdn_fwd;
2196  fqdn_rev = hook_fqdn_rev;
2197 
2198  // If there's an outbound host-name option in the response we
2199  // need to updated it with the new host name.
2200  OptionStringPtr hostname_opt = boost::dynamic_pointer_cast<OptionString>
2201  (resp->getOption(DHO_HOST_NAME));
2202  if (hostname_opt) {
2203  hostname_opt->setValue(hook_hostname);
2204  }
2205 
2206  // If there's an outbound FQDN option in the response we need
2207  // to update it with the new host name.
2208  Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn>
2209  (resp->getOption(DHO_FQDN));
2210  if (fqdn) {
2211  fqdn->setDomainName(hook_hostname, Option4ClientFqdn::FULL);
2212  // Hook disabled updates, Set flags back to client accordingly.
2213  fqdn->setFlag(Option4ClientFqdn::FLAG_S, 0);
2214  fqdn->setFlag(Option4ClientFqdn::FLAG_N, 1);
2215  }
2216  }
2217  }
2218 
2219  // Update the context
2220  auto ctx = ex.getContext();
2221  ctx->fwd_dns_update_ = fqdn_fwd;
2222  ctx->rev_dns_update_ = fqdn_rev;
2223  ctx->hostname_ = hostname;
2224 
2225  } catch (const Exception& e) {
2226  // In some rare cases it is possible that the client's name processing
2227  // fails. For example, the Hostname option may be malformed, or there
2228  // may be an error in the server's logic which would cause multiple
2229  // attempts to add the same option to the response message. This
2230  // error message aggregates all these errors so they can be diagnosed
2231  // from the log. We don't want to throw an exception here because,
2232  // it will impact the processing of the whole packet. We rather want
2233  // the processing to continue, even if the client's name is wrong.
2235  .arg(ex.getQuery()->getLabel())
2236  .arg(e.what());
2237  }
2238 }
2239 
2240 void
2241 Dhcpv4Srv::processClientFqdnOption(Dhcpv4Exchange& ex) {
2242  // Obtain the FQDN option from the client's message.
2243  Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
2244  Option4ClientFqdn>(ex.getQuery()->getOption(DHO_FQDN));
2245 
2247  .arg(ex.getQuery()->getLabel())
2248  .arg(fqdn->toText());
2249 
2250  // Create the DHCPv4 Client FQDN Option to be included in the server's
2251  // response to a client.
2252  Option4ClientFqdnPtr fqdn_resp(new Option4ClientFqdn(*fqdn));
2253 
2254  // Set the server S, N, and O flags based on client's flags and
2255  // current configuration.
2257  d2_mgr.adjustFqdnFlags<Option4ClientFqdn>(*fqdn, *fqdn_resp,
2258  *(ex.getContext()->getDdnsParams()));
2259  // Carry over the client's E flag.
2262 
2263  if (ex.getContext()->currentHost() &&
2264  !ex.getContext()->currentHost()->getHostname().empty()) {
2266  fqdn_resp->setDomainName(d2_mgr.qualifyName(ex.getContext()->currentHost()->getHostname(),
2267  *(ex.getContext()->getDdnsParams()), true),
2269 
2270  } else {
2271  // Adjust the domain name based on domain name value and type sent by the
2272  // client and current configuration.
2273  d2_mgr.adjustDomainName<Option4ClientFqdn>(*fqdn, *fqdn_resp,
2274  *(ex.getContext()->getDdnsParams()));
2275  }
2276 
2277  // Add FQDN option to the response message. Note that, there may be some
2278  // cases when server may choose not to include the FQDN option in a
2279  // response to a client. In such cases, the FQDN should be removed from the
2280  // outgoing message. In theory we could cease to include the FQDN option
2281  // in this function until it is confirmed that it should be included.
2282  // However, we include it here for simplicity. Functions used to acquire
2283  // lease for a client will scan the response message for FQDN and if it
2284  // is found they will take necessary actions to store the FQDN information
2285  // in the lease database as well as to generate NameChangeRequests to DNS.
2286  // If we don't store the option in the response message, we will have to
2287  // propagate it in the different way to the functions which acquire the
2288  // lease. This would require modifications to the API of this class.
2290  .arg(ex.getQuery()->getLabel())
2291  .arg(fqdn_resp->toText());
2292  ex.getResponse()->addOption(fqdn_resp);
2293 }
2294 
2295 void
2296 Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) {
2297  // Fetch D2 configuration.
2299 
2300  // Obtain the Hostname option from the client's message.
2301  OptionStringPtr opt_hostname = boost::dynamic_pointer_cast<OptionString>
2302  (ex.getQuery()->getOption(DHO_HOST_NAME));
2303 
2304  if (opt_hostname) {
2306  .arg(ex.getQuery()->getLabel())
2307  .arg(opt_hostname->getValue());
2308  }
2309 
2311 
2312  // Hostname reservations take precedence over any other configuration,
2313  // i.e. DDNS configuration. If we have a reserved hostname we should
2314  // use it and send it back.
2315  if (ctx->currentHost() && !ctx->currentHost()->getHostname().empty()) {
2316  // Qualify if there is a suffix configured.
2317  std::string hostname = d2_mgr.qualifyName(ctx->currentHost()->getHostname(),
2318  *(ex.getContext()->getDdnsParams()), false);
2319  // Convert it to lower case.
2320  boost::algorithm::to_lower(hostname);
2322  .arg(ex.getQuery()->getLabel())
2323  .arg(hostname);
2324 
2325  // Add it to the response
2326  OptionStringPtr opt_hostname_resp(new OptionString(Option::V4, DHO_HOST_NAME, hostname));
2327  ex.getResponse()->addOption(opt_hostname_resp);
2328 
2329  // We're done here.
2330  return;
2331  }
2332 
2333  // There is no reservation for this client however there is still a
2334  // possibility that we'll have to send hostname option to this client
2335  // if the client has included hostname option or the configuration of
2336  // the server requires that we send the option regardless.
2337  D2ClientConfig::ReplaceClientNameMode replace_name_mode =
2338  ex.getContext()->getDdnsParams()->getReplaceClientNameMode();
2339 
2340  // If we don't have a hostname then either we'll supply it or do nothing.
2341  if (!opt_hostname) {
2342  // If we're configured to supply it then add it to the response.
2343  // Use the root domain to signal later on that we should replace it.
2344  if (replace_name_mode == D2ClientConfig::RCM_ALWAYS ||
2345  replace_name_mode == D2ClientConfig::RCM_WHEN_NOT_PRESENT) {
2348  .arg(ex.getQuery()->getLabel());
2349  OptionStringPtr opt_hostname_resp(new OptionString(Option::V4,
2350  DHO_HOST_NAME,
2351  "."));
2352  ex.getResponse()->addOption(opt_hostname_resp);
2353  }
2354 
2355  return;
2356  }
2357 
2358  // Client sent us a hostname option so figure out what to do with it.
2360  .arg(ex.getQuery()->getLabel())
2361  .arg(opt_hostname->getValue());
2362 
2363  std::string hostname = isc::util::str::trim(opt_hostname->getValue());
2364  unsigned int label_count;
2365 
2366  try {
2367  // Parsing into labels can throw on malformed content so we're
2368  // going to explicitly catch that here.
2369  label_count = OptionDataTypeUtil::getLabelCount(hostname);
2370  } catch (const std::exception& exc) {
2372  .arg(ex.getQuery()->getLabel())
2373  .arg(exc.what());
2374  return;
2375  }
2376 
2377  // The hostname option sent by the client should be at least 1 octet long.
2378  // If it isn't we ignore this option. (Per RFC 2131, section 3.14)
2381  if (label_count == 0) {
2383  .arg(ex.getQuery()->getLabel());
2384  return;
2385  }
2386 
2387  // Stores the value we eventually use, so we can send it back.
2388  OptionStringPtr opt_hostname_resp;
2389 
2390  // The hostname option may be unqualified or fully qualified. The lab_count
2391  // holds the number of labels for the name. The number of 1 means that
2392  // there is only root label "." (even for unqualified names, as the
2393  // getLabelCount function treats each name as a fully qualified one).
2394  // By checking the number of labels present in the hostname we may infer
2395  // whether client has sent the fully qualified or unqualified hostname.
2396 
2397  if ((replace_name_mode == D2ClientConfig::RCM_ALWAYS ||
2398  replace_name_mode == D2ClientConfig::RCM_WHEN_PRESENT)
2399  || label_count < 2) {
2400  // Set to root domain to signal later on that we should replace it.
2401  // DHO_HOST_NAME is a string option which cannot be empty.
2409  opt_hostname_resp.reset(new OptionString(Option::V4, DHO_HOST_NAME, "."));
2410  } else {
2411  // Sanitize the name the client sent us, if we're configured to do so.
2413  ex.getContext()->getDdnsParams()->getHostnameSanitizer();
2414 
2415  if (sanitizer) {
2416  hostname = sanitizer->scrub(hostname);
2417  }
2418 
2419  // Convert hostname to lower case.
2420  boost::algorithm::to_lower(hostname);
2421 
2422  if (label_count == 2) {
2423  // If there are two labels, it means that the client has specified
2424  // the unqualified name. We have to concatenate the unqualified name
2425  // with the domain name. The false value passed as a second argument
2426  // indicates that the trailing dot should not be appended to the
2427  // hostname. We don't want to append the trailing dot because
2428  // we don't know whether the hostname is partial or not and some
2429  // clients do not handle the hostnames with the trailing dot.
2430  opt_hostname_resp.reset(
2432  d2_mgr.qualifyName(hostname, *(ex.getContext()->getDdnsParams()),
2433  false)));
2434  } else {
2435  opt_hostname_resp.reset(new OptionString(Option::V4, DHO_HOST_NAME, hostname));
2436  }
2437  }
2438 
2440  .arg(ex.getQuery()->getLabel())
2441  .arg(opt_hostname_resp->getValue());
2442  ex.getResponse()->addOption(opt_hostname_resp);
2443 }
2444 
2445 void
2447  const Lease4Ptr& old_lease,
2448  const DdnsParams& ddns_params) {
2449  if (!lease) {
2451  "NULL lease specified when creating NameChangeRequest");
2452  }
2453 
2454  // Nothing to do if updates are not enabled.
2455  if (!ddns_params.getEnableUpdates()) {
2456  return;
2457  }
2458 
2459  if (!old_lease || ddns_params.getUpdateOnRenew() || !lease->hasIdenticalFqdn(*old_lease)) {
2460  if (old_lease) {
2461  // Queue's up a remove of the old lease's DNS (if needed)
2462  queueNCR(CHG_REMOVE, old_lease);
2463  }
2464 
2465  // We may need to generate the NameChangeRequest for the new lease. It
2466  // will be generated only if hostname is set and if forward or reverse
2467  // update has been requested.
2468  queueNCR(CHG_ADD, lease);
2469  }
2470 }
2471 
2472 void
2474  // Get the pointers to the query and the response messages.
2475  Pkt4Ptr query = ex.getQuery();
2476  Pkt4Ptr resp = ex.getResponse();
2477 
2478  // Get the context.
2480 
2481  // Subnet should have been already selected when the context was created.
2482  Subnet4Ptr subnet = ctx->subnet_;
2483  if (!subnet) {
2484  // This particular client is out of luck today. We do not have
2485  // information about the subnet he is connected to. This likely means
2486  // misconfiguration of the server (or some relays).
2487 
2488  // Perhaps this should be logged on some higher level?
2490  .arg(query->getLabel())
2491  .arg(query->getRemoteAddr().toText())
2492  .arg(query->getName());
2493  resp->setType(DHCPNAK);
2494  resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2495  return;
2496  }
2497 
2498  // Get the server identifier. It will be used to determine the state
2499  // of the client.
2500  OptionCustomPtr opt_serverid = boost::dynamic_pointer_cast<
2501  OptionCustom>(query->getOption(DHO_DHCP_SERVER_IDENTIFIER));
2502 
2503  // Check if the client has sent a requested IP address option or
2504  // ciaddr.
2505  OptionCustomPtr opt_requested_address = boost::dynamic_pointer_cast<
2506  OptionCustom>(query->getOption(DHO_DHCP_REQUESTED_ADDRESS));
2508  if (opt_requested_address) {
2509  hint = opt_requested_address->readAddress();
2510 
2511  } else if (!query->getCiaddr().isV4Zero()) {
2512  hint = query->getCiaddr();
2513 
2514  }
2515 
2516  HWAddrPtr hwaddr = query->getHWAddr();
2517 
2518  // "Fake" allocation is processing of DISCOVER message. We pretend to do an
2519  // allocation, but we do not put the lease in the database. That is ok,
2520  // because we do not guarantee that the user will get that exact lease. If
2521  // the user selects this server to do actual allocation (i.e. sends REQUEST)
2522  // it should include this hint. That will help us during the actual lease
2523  // allocation.
2524  bool fake_allocation = (query->getType() == DHCPDISCOVER);
2525  Subnet4Ptr original_subnet = subnet;
2526 
2527  // Get client-id. It is not mandatory in DHCPv4.
2528  ClientIdPtr client_id = ex.getContext()->clientid_;
2529 
2530  // If there is no server id and there is a Requested IP Address option
2531  // the client is in the INIT-REBOOT state in which the server has to
2532  // determine whether the client's notion of the address is correct
2533  // and whether the client is known, i.e., has a lease.
2534  if (!fake_allocation && !opt_serverid && opt_requested_address) {
2535 
2537  .arg(query->getLabel())
2538  .arg(hint.toText());
2539 
2540  Lease4Ptr lease;
2541 
2542  auto const& classes = query->getClasses();
2543 
2544  // We used to issue a separate query (two actually: one for client-id
2545  // and another one for hw-addr for) each subnet in the shared network.
2546  // That was horribly inefficient if the client didn't have any lease
2547  // (or there were many subnets and the client happened to be in one
2548  // of the last subnets).
2549  //
2550  // We now issue at most two queries: get all the leases for specific
2551  // client-id and then get all leases for specific hw-address.
2552  if (client_id) {
2553 
2554  // Get all the leases for this client-id
2555  Lease4Collection leases_client_id = LeaseMgrFactory::instance().getLease4(*client_id);
2556  if (!leases_client_id.empty()) {
2557  Subnet4Ptr s = original_subnet;
2558 
2559  // Among those returned try to find a lease that belongs to
2560  // current shared network.
2561  while (s) {
2562  for (auto l = leases_client_id.begin(); l != leases_client_id.end(); ++l) {
2563  if ((*l)->subnet_id_ == s->getID()) {
2564  lease = *l;
2565  break;
2566  }
2567  }
2568 
2569  if (lease) {
2570  break;
2571 
2572  } else {
2573  s = s->getNextSubnet(original_subnet, classes);
2574  }
2575  }
2576  }
2577  }
2578 
2579  // If we haven't found a lease yet, try again by hardware-address.
2580  // The logic is the same.
2581  if (!lease && hwaddr) {
2582 
2583  // Get all leases for this particular hw-address.
2584  Lease4Collection leases_hwaddr = LeaseMgrFactory::instance().getLease4(*hwaddr);
2585  if (!leases_hwaddr.empty()) {
2586  Subnet4Ptr s = original_subnet;
2587 
2588  // Pick one that belongs to a subnet in this shared network.
2589  while (s) {
2590  for (auto l = leases_hwaddr.begin(); l != leases_hwaddr.end(); ++l) {
2591  if ((*l)->subnet_id_ == s->getID()) {
2592  lease = *l;
2593  break;
2594  }
2595  }
2596 
2597  if (lease) {
2598  break;
2599 
2600  } else {
2601  s = s->getNextSubnet(original_subnet, classes);
2602  }
2603  }
2604  }
2605  }
2606 
2607  // Check the first error case: unknown client. We check this before
2608  // validating the address sent because we don't want to respond if
2609  // we don't know this client, except if we're authoritative.
2610  bool authoritative = original_subnet->getAuthoritative();
2611  bool known_client = lease && lease->belongsToClient(hwaddr, client_id);
2612  if (!authoritative && !known_client) {
2615  .arg(query->getLabel())
2616  .arg(hint.toText());
2617 
2618  ex.deleteResponse();
2619  return;
2620  }
2621 
2622  // If we know this client, check if his notion of the IP address is
2623  // correct, if we don't know him, check if we are authoritative.
2624  if ((known_client && (lease->addr_ != hint)) ||
2625  (!known_client && authoritative)) {
2628  .arg(query->getLabel())
2629  .arg(hint.toText());
2630 
2631  resp->setType(DHCPNAK);
2632  resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2633  return;
2634  }
2635  }
2636 
2637  CalloutHandlePtr callout_handle = getCalloutHandle(query);
2638 
2639  // We need to set these values in the context as they haven't been set yet.
2640  ctx->requested_address_ = hint;
2641  ctx->fake_allocation_ = fake_allocation;
2642  ctx->callout_handle_ = callout_handle;
2643 
2644  // If client query contains an FQDN or Hostname option, server
2645  // should respond to the client with the appropriate FQDN or Hostname
2646  // option to indicate if it takes responsibility for the DNS updates.
2647  // This is also the source for the hostname and dns flags that are
2648  // initially added to the lease. In most cases, this information is
2649  // good now. If we end up changing subnets in allocation we'll have to
2650  // do it again and then update the lease.
2651  processClientName(ex);
2652 
2653  // Get a lease.
2654  Lease4Ptr lease = alloc_engine_->allocateLease4(*ctx);
2655 
2656  // Tracks whether or not the client name (FQDN or host) has changed since
2657  // the lease was allocated.
2658  bool client_name_changed = false;
2659 
2660  // Subnet may be modified by the allocation engine, if the initial subnet
2661  // belongs to a shared network.
2662  if (subnet && ctx->subnet_ && subnet->getID() != ctx->subnet_->getID()) {
2663  SharedNetwork4Ptr network;
2664  subnet->getSharedNetwork(network);
2666  .arg(query->getLabel())
2667  .arg(subnet->toText())
2668  .arg(ctx->subnet_->toText())
2669  .arg(network ? network->getName() : "<no network?>");
2670 
2671  subnet = ctx->subnet_;
2672 
2673  if (lease) {
2674  // We changed subnets and that means DDNS parameters might be different
2675  // so we need to rerun client name processing logic. Arguably we could
2676  // compare DDNS parameters for both subnets and then decide if we need
2677  // to rerun the name logic, but that's not likely to be any faster than
2678  // just re-running the name logic. @todo When inherited parameter
2679  // performance is improved this argument could be revisited.
2680  // Another case is the new subnet has a reserved hostname.
2681 
2682  // First, we need to remove the prior values from the response and reset
2683  // those in context, to give processClientName a clean slate.
2684  resp->delOption(DHO_FQDN);
2685  resp->delOption(DHO_HOST_NAME);
2686  ctx->hostname_ = "";
2687  ctx->fwd_dns_update_ = false;
2688  ctx->rev_dns_update_ = false;
2689 
2690  // Regenerate the name and dns flags.
2691  processClientName(ex);
2692 
2693  // If the results are different from the values already on the
2694  // lease, flag it so the lease gets updated down below.
2695  if ((lease->hostname_ != ctx->hostname_) ||
2696  (lease->fqdn_fwd_ != ctx->fwd_dns_update_) ||
2697  (lease->fqdn_rev_ != ctx->rev_dns_update_)) {
2698  lease->hostname_ = ctx->hostname_;
2699  lease->fqdn_fwd_ = ctx->fwd_dns_update_;
2700  lease->fqdn_rev_ = ctx->rev_dns_update_;
2701  client_name_changed = true;
2702  }
2703  }
2704  }
2705 
2706  if (lease) {
2707  // We have a lease! Let's set it in the packet and send it back to
2708  // the client.
2709  if (fake_allocation) {
2711  .arg(query->getLabel())
2712  .arg(lease->addr_.toText());
2713  } else {
2715  .arg(query->getLabel())
2716  .arg(lease->addr_.toText())
2717  .arg(Lease::lifetimeToText(lease->valid_lft_));
2718  }
2719 
2720  // We're logging this here, because this is the place where we know
2721  // which subnet has been actually used for allocation. If the
2722  // client identifier matching is disabled, we want to make sure that
2723  // the user is notified.
2724  if (!ctx->subnet_->getMatchClientId()) {
2726  .arg(ctx->query_->getLabel())
2727  .arg(ctx->subnet_->getID());
2728  }
2729 
2730  resp->setYiaddr(lease->addr_);
2731 
2736  if (!fake_allocation) {
2737  // If this is a renewing client it will set a ciaddr which the
2738  // server may include in the response. If this is a new allocation
2739  // the client will set ciaddr to 0 and this will also be propagated
2740  // to the server's resp.
2741  resp->setCiaddr(query->getCiaddr());
2742  }
2743 
2744  // We may need to update FQDN or hostname if the server is to generate
2745  // a new name from the allocated IP address or if the allocation engine
2746  // switched to a different subnet within a shared network.
2747  postAllocateNameUpdate(ctx, lease, query, resp, client_name_changed);
2748 
2749  // Reuse the lease if possible.
2750  if (lease->reuseable_valid_lft_ > 0) {
2751  lease->valid_lft_ = lease->reuseable_valid_lft_;
2753  .arg(query->getLabel())
2754  .arg(lease->addr_.toText())
2755  .arg(Lease::lifetimeToText(lease->valid_lft_));
2756  }
2757 
2758  // IP Address Lease time (type 51)
2760  lease->valid_lft_));
2761  resp->addOption(opt);
2762 
2763  // Subnet mask (type 1)
2764  resp->addOption(getNetmaskOption(subnet));
2765 
2766  // Set T1 and T2 per configuration.
2767  setTeeTimes(lease, subnet, resp);
2768 
2769  // Create NameChangeRequests if this is a real allocation.
2770  if (!fake_allocation) {
2771  try {
2772  createNameChangeRequests(lease, ctx->old_lease_,
2773  *ex.getContext()->getDdnsParams());
2774  } catch (const Exception& ex) {
2776  .arg(query->getLabel())
2777  .arg(ex.what());
2778  }
2779  }
2780 
2781  } else {
2782  // Allocation engine did not allocate a lease. The engine logged
2783  // cause of that failure.
2784  if (ctx->unknown_requested_addr_) {
2785  Subnet4Ptr s = original_subnet;
2786  // Address might have been rejected via class guard (i.e. not
2787  // allowed for this client). We need to determine if we truly
2788  // do not know about the address or whether this client just
2789  // isn't allowed to have that address. We should only DHCPNAK
2790  // For the latter.
2791  while (s) {
2792  if (s->inPool(Lease::TYPE_V4, hint)) {
2793  break;
2794  }
2795 
2796  s = s->getNextSubnet(original_subnet);
2797  }
2798 
2799  // If we didn't find a subnet, it's not an address we know about
2800  // so we drop the DHCPNAK.
2801  if (!s) {
2804  .arg(query->getLabel())
2805  .arg(query->getCiaddr().toText())
2806  .arg(opt_requested_address ?
2807  opt_requested_address->readAddress().toText() : "(no address)");
2808  ex.deleteResponse();
2809  return;
2810  }
2811  }
2812 
2813  LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, fake_allocation ?
2815  .arg(query->getLabel())
2816  .arg(query->getCiaddr().toText())
2817  .arg(opt_requested_address ?
2818  opt_requested_address->readAddress().toText() : "(no address)");
2819 
2820  resp->setType(DHCPNAK);
2821  resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2822 
2823  resp->delOption(DHO_FQDN);
2824  resp->delOption(DHO_HOST_NAME);
2825  }
2826 }
2827 
2828 void
2830  const Pkt4Ptr& query, const Pkt4Ptr& resp, bool client_name_changed) {
2831  // We may need to update FQDN or hostname if the server is to generate
2832  // new name from the allocated IP address or if the allocation engine
2833  // has switched to a different subnet within a shared network. Get
2834  // FQDN and hostname options from the response.
2835  OptionStringPtr opt_hostname;
2836  Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
2837  Option4ClientFqdn>(resp->getOption(DHO_FQDN));
2838  if (!fqdn) {
2839  opt_hostname = boost::dynamic_pointer_cast<OptionString>(resp->getOption(DHO_HOST_NAME));
2840  if (!opt_hostname) {
2841  // We don't have either one, nothing to do.
2842  return;
2843  }
2844  }
2845 
2846  // Empty hostname on the lease means we need to generate it.
2847  if (lease->hostname_.empty()) {
2848  // Note that if we have received the hostname option, rather than
2849  // Client FQDN the trailing dot is not appended to the generated
2850  // hostname because some clients don't handle the trailing dot in
2851  // the hostname. Whether the trailing dot is appended or not is
2852  // controlled by the second argument to the generateFqdn().
2853  lease->hostname_ = CfgMgr::instance().getD2ClientMgr()
2854  .generateFqdn(lease->addr_, *(ctx->getDdnsParams()), static_cast<bool>(fqdn));
2855 
2857  .arg(query->getLabel())
2858  .arg(lease->hostname_);
2859 
2860  client_name_changed = true;
2861  }
2862 
2863  if (client_name_changed) {
2864  // The operations below are rather safe, but we want to catch
2865  // any potential exceptions (e.g. invalid lease database backend
2866  // implementation) and log an error.
2867  try {
2868  if (!ctx->fake_allocation_) {
2869  // The lease can't be reused.
2870  lease->reuseable_valid_lft_ = 0;
2871 
2872  // The lease update should be safe, because the lease should
2873  // be already in the database. In most cases the exception
2874  // would be thrown if the lease was missing.
2876  }
2877 
2878  // The name update in the outbound option should be also safe,
2879  // because the generated name is well formed.
2880  if (fqdn) {
2881  fqdn->setDomainName(lease->hostname_, Option4ClientFqdn::FULL);
2882  } else {
2883  opt_hostname->setValue(lease->hostname_);
2884  }
2885  } catch (const Exception& ex) {
2887  .arg(query->getLabel())
2888  .arg(lease->hostname_)
2889  .arg(ex.what());
2890  }
2891  }
2892 }
2893 
2895 void
2896 Dhcpv4Srv::setTeeTimes(const Lease4Ptr& lease, const Subnet4Ptr& subnet, Pkt4Ptr resp) {
2897 
2898  uint32_t t2_time = 0;
2899  // If T2 is explicitly configured we'll use try value.
2900  if (!subnet->getT2().unspecified()) {
2901  t2_time = subnet->getT2();
2902  } else if (subnet->getCalculateTeeTimes()) {
2903  // Calculating tee times is enabled, so calculated it.
2904  t2_time = static_cast<uint32_t>(round(subnet->getT2Percent() * (lease->valid_lft_)));
2905  }
2906 
2907  // Send the T2 candidate value only if it's sane: to be sane it must be less than
2908  // the valid life time.
2909  uint32_t timer_ceiling = lease->valid_lft_;
2910  if (t2_time > 0 && t2_time < timer_ceiling) {
2912  resp->addOption(t2);
2913  // When we send T2, timer ceiling for T1 becomes T2.
2914  timer_ceiling = t2_time;
2915  }
2916 
2917  uint32_t t1_time = 0;
2918  // If T1 is explicitly configured we'll use try value.
2919  if (!subnet->getT1().unspecified()) {
2920  t1_time = subnet->getT1();
2921  } else if (subnet->getCalculateTeeTimes()) {
2922  // Calculating tee times is enabled, so calculate it.
2923  t1_time = static_cast<uint32_t>(round(subnet->getT1Percent() * (lease->valid_lft_)));
2924  }
2925 
2926  // Send T1 if it's sane: If we sent T2, T1 must be less than that. If not it must be
2927  // less than the valid life time.
2928  if (t1_time > 0 && t1_time < timer_ceiling) {
2930  resp->addOption(t1);
2931  }
2932 }
2933 
2934 uint16_t
2936 
2937  // Look for a relay-port RAI sub-option in the query.
2938  const Pkt4Ptr& query = ex.getQuery();
2939  const OptionPtr& rai = query->getOption(DHO_DHCP_AGENT_OPTIONS);
2940  if (rai && rai->getOption(RAI_OPTION_RELAY_PORT)) {
2941  // Got the sub-option so use the remote port set by the relay.
2942  return (query->getRemotePort());
2943  }
2944  return (0);
2945 }
2946 
2947 void
2949  adjustRemoteAddr(ex);
2950 
2951  // Initialize the pointers to the client's message and the server's
2952  // response.
2953  Pkt4Ptr query = ex.getQuery();
2954  Pkt4Ptr response = ex.getResponse();
2955 
2956  // The DHCPINFORM is generally unicast to the client. The only situation
2957  // when the server is unable to unicast to the client is when the client
2958  // doesn't include ciaddr and the message is relayed. In this case the
2959  // server has to reply via relay agent. For other messages we send back
2960  // through relay if message is relayed, and unicast to the client if the
2961  // message is not relayed.
2962  // If client port was set from the command line enforce all responses
2963  // to it. Of course it is only for testing purposes.
2964  // Note that the call to this function may throw if invalid combination
2965  // of hops and giaddr is found (hops = 0 if giaddr = 0 and hops != 0 if
2966  // giaddr != 0). The exception will propagate down and eventually cause the
2967  // packet to be discarded.
2968  if (client_port_) {
2969  response->setRemotePort(client_port_);
2970  } else if (((query->getType() == DHCPINFORM) &&
2971  ((!query->getCiaddr().isV4Zero()) ||
2972  (!query->isRelayed() && !query->getRemoteAddr().isV4Zero()))) ||
2973  ((query->getType() != DHCPINFORM) && !query->isRelayed())) {
2974  response->setRemotePort(DHCP4_CLIENT_PORT);
2975 
2976  } else {
2977  // RFC 8357 section 5.1
2978  uint16_t relay_port = checkRelayPort(ex);
2979  response->setRemotePort(relay_port ? relay_port : DHCP4_SERVER_PORT);
2980  }
2981 
2982  CfgIfacePtr cfg_iface = CfgMgr::instance().getCurrentCfg()->getCfgIface();
2983  if (query->isRelayed() &&
2984  (cfg_iface->getSocketType() == CfgIface::SOCKET_UDP) &&
2985  (cfg_iface->getOutboundIface() == CfgIface::USE_ROUTING)) {
2986 
2987  // Mark the response to follow routing
2988  response->setLocalAddr(IOAddress::IPV4_ZERO_ADDRESS());
2989  response->resetIndex();
2990  // But keep the interface name
2991  response->setIface(query->getIface());
2992 
2993  } else {
2994 
2995  IOAddress local_addr = query->getLocalAddr();
2996 
2997  // In many cases the query is sent to a broadcast address. This address
2998  // appears as a local address in the query message. We can't simply copy
2999  // this address to a response message and use it as a source address.
3000  // Instead we will need to use the address assigned to the interface
3001  // on which the query has been received. In other cases, we will just
3002  // use this address as a source address for the response.
3003  // Do the same for DHCPv4-over-DHCPv6 exchanges.
3004  if (local_addr.isV4Bcast() || query->isDhcp4o6()) {
3005  local_addr = IfaceMgr::instance().getSocket(query).addr_;
3006  }
3007 
3008  // We assume that there is an appropriate socket bound to this address
3009  // and that the address is correct. This is safe assumption because
3010  // the local address of the query is set when the query is received.
3011  // The query sent to an incorrect address wouldn't have been received.
3012  // However, if socket is closed for this address between the reception
3013  // of the query and sending a response, the IfaceMgr should detect it
3014  // and return an error.
3015  response->setLocalAddr(local_addr);
3016  // In many cases the query is sent to a broadcast address. This address
3017  // appears as a local address in the query message. Therefore we can't
3018  // simply copy local address from the query and use it as a source
3019  // address for the response. Instead, we have to check what address our
3020  // socket is bound to and use it as a source address. This operation
3021  // may throw if for some reason the socket is closed.
3024  response->setIndex(query->getIndex());
3025  response->setIface(query->getIface());
3026  }
3027 
3028  if (server_port_) {
3029  response->setLocalPort(server_port_);
3030  } else {
3031  response->setLocalPort(DHCP4_SERVER_PORT);
3032  }
3033 }
3034 
3035 void
3037  // Initialize the pointers to the client's message and the server's
3038  // response.
3039  Pkt4Ptr query = ex.getQuery();
3040  Pkt4Ptr response = ex.getResponse();
3041 
3042  // DHCPv4-over-DHCPv6 is simple
3043  if (query->isDhcp4o6()) {
3044  response->setRemoteAddr(query->getRemoteAddr());
3045  return;
3046  }
3047 
3048  // The DHCPINFORM is slightly different than other messages in a sense
3049  // that the server should always unicast the response to the ciaddr.
3050  // It appears however that some clients don't set the ciaddr. We still
3051  // want to provision these clients and we do what we can't to send the
3052  // packet to the address where client can receive it.
3053  if (query->getType() == DHCPINFORM) {
3054  // If client adheres to RFC2131 it will set the ciaddr and in this
3055  // case we always unicast our response to this address.
3056  if (!query->getCiaddr().isV4Zero()) {
3057  response->setRemoteAddr(query->getCiaddr());
3058 
3059  // If we received DHCPINFORM via relay and the ciaddr is not set we
3060  // will try to send the response via relay. The caveat is that the
3061  // relay will not have any idea where to forward the packet because
3062  // the yiaddr is likely not set. So, the broadcast flag is set so
3063  // as the response may be broadcast.
3064  } else if (query->isRelayed()) {
3065  response->setRemoteAddr(query->getGiaddr());
3066  response->setFlags(response->getFlags() | BOOTP_BROADCAST);
3067 
3068  // If there is no ciaddr and no giaddr the only thing we can do is
3069  // to use the source address of the packet.
3070  } else {
3071  response->setRemoteAddr(query->getRemoteAddr());
3072  }
3073  // Remote address is now set so return.
3074  return;
3075  }
3076 
3077  // If received relayed message, server responds to the relay address.
3078  if (query->isRelayed()) {
3079  // The client should set the ciaddr when sending the DHCPINFORM
3080  // but in case he didn't, the relay may not be able to determine the
3081  // address of the client, because yiaddr is not set when responding
3082  // to Confirm and the only address available was the source address
3083  // of the client. The source address is however not used here because
3084  // the message is relayed. Therefore, we set the BROADCAST flag so
3085  // as the relay can broadcast the packet.
3086  if ((query->getType() == DHCPINFORM) &&
3087  query->getCiaddr().isV4Zero()) {
3088  response->setFlags(BOOTP_BROADCAST);
3089  }
3090  response->setRemoteAddr(query->getGiaddr());
3091 
3092  // If giaddr is 0 but client set ciaddr, server should unicast the
3093  // response to ciaddr.
3094  } else if (!query->getCiaddr().isV4Zero()) {
3095  response->setRemoteAddr(query->getCiaddr());
3096 
3097  // We can't unicast the response to the client when sending DHCPNAK,
3098  // because we haven't allocated address for him. Therefore,
3099  // DHCPNAK is broadcast.
3100  } else if (response->getType() == DHCPNAK) {
3101  response->setRemoteAddr(IOAddress::IPV4_BCAST_ADDRESS());
3102 
3103  // If yiaddr is set it means that we have created a lease for a client.
3104  } else if (!response->getYiaddr().isV4Zero()) {
3105  // If the broadcast bit is set in the flags field, we have to
3106  // send the response to broadcast address. Client may have requested it
3107  // because it doesn't support reception of messages on the interface
3108  // which doesn't have an address assigned. The other case when response
3109  // must be broadcasted is when our server does not support responding
3110  // directly to a client without address assigned.
3111  const bool bcast_flag = ((query->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
3112  if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
3113  response->setRemoteAddr(IOAddress::IPV4_BCAST_ADDRESS());
3114 
3115  // Client cleared the broadcast bit and we support direct responses
3116  // so we should unicast the response to a newly allocated address -
3117  // yiaddr.
3118  } else {
3119  response->setRemoteAddr(response ->getYiaddr());
3120 
3121  }
3122 
3123  // In most cases, we should have the remote address found already. If we
3124  // found ourselves at this point, the rational thing to do is to respond
3125  // to the address we got the query from.
3126  } else {
3127  response->setRemoteAddr(query->getRemoteAddr());
3128  }
3129 
3130  // For testing *only*.
3131  if (getSendResponsesToSource()) {
3132  response->setRemoteAddr(query->getRemoteAddr());
3133  }
3134 }
3135 
3136 void
3138  Pkt4Ptr query = ex.getQuery();
3139  Pkt4Ptr response = ex.getResponse();
3140 
3141  // Step 1: Start with fixed fields defined on subnet level.
3142  Subnet4Ptr subnet = ex.getContext()->subnet_;
3143  if (subnet) {
3144  IOAddress subnet_next_server = subnet->getSiaddr();
3145  if (!subnet_next_server.isV4Zero()) {
3146  response->setSiaddr(subnet_next_server);
3147  }
3148 
3149  const string& sname = subnet->getSname();
3150  if (!sname.empty()) {
3151  // Converting string to (const uint8_t*, size_t len) format is
3152  // tricky. reinterpret_cast is not the most elegant solution,
3153  // but it does avoid us making unnecessary copy. We will convert
3154  // sname and file fields in Pkt4 to string one day and life
3155  // will be easier.
3156  response->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
3157  sname.size());
3158  }
3159 
3160  const string& filename = subnet->getFilename();
3161  if (!filename.empty()) {
3162  // Converting string to (const uint8_t*, size_t len) format is
3163  // tricky. reinterpret_cast is not the most elegant solution,
3164  // but it does avoid us making unnecessary copy. We will convert
3165  // sname and file fields in Pkt4 to string one day and life
3166  // will be easier.
3167  response->setFile(reinterpret_cast<const uint8_t*>(filename.c_str()),
3168  filename.size());
3169  }
3170  }
3171 
3172  // Step 2: Try to set the values based on classes.
3173  // Any values defined in classes will override those from subnet level.
3174  const ClientClasses classes = query->getClasses();
3175  if (!classes.empty()) {
3176 
3177  // Let's get class definitions
3178  const ClientClassDictionaryPtr& dict =
3179  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3180 
3181  // Now we need to iterate over the classes assigned to the
3182  // query packet and find corresponding class definitions for it.
3183  // We want the first value found for each field. We track how
3184  // many we've found so we can stop if we have all three.
3185  IOAddress next_server = IOAddress::IPV4_ZERO_ADDRESS();
3186  string sname;
3187  string filename;
3188  size_t found_cnt = 0; // How many fields we have found.
3189  for (ClientClasses::const_iterator name = classes.cbegin();
3190  name != classes.cend() && found_cnt < 3; ++name) {
3191 
3192  ClientClassDefPtr cl = dict->findClass(*name);
3193  if (!cl) {
3194  // Let's skip classes that don't have definitions. Currently
3195  // these are automatic classes VENDOR_CLASS_something, but there
3196  // may be other classes assigned under other circumstances, e.g.
3197  // by hooks.
3198  continue;
3199  }
3200 
3201  if (next_server == IOAddress::IPV4_ZERO_ADDRESS()) {
3202  next_server = cl->getNextServer();
3203  if (!next_server.isV4Zero()) {
3204  response->setSiaddr(next_server);
3205  found_cnt++;
3206  }
3207  }
3208 
3209  if (sname.empty()) {
3210  sname = cl->getSname();
3211  if (!sname.empty()) {
3212  // Converting string to (const uint8_t*, size_t len) format is
3213  // tricky. reinterpret_cast is not the most elegant solution,
3214  // but it does avoid us making unnecessary copy. We will convert
3215  // sname and file fields in Pkt4 to string one day and life
3216  // will be easier.
3217  response->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
3218  sname.size());
3219  found_cnt++;
3220  }
3221  }
3222 
3223  if (filename.empty()) {
3224  filename = cl->getFilename();
3225  if (!filename.empty()) {
3226  // Converting string to (const uint8_t*, size_t len) format is
3227  // tricky. reinterpret_cast is not the most elegant solution,
3228  // but it does avoid us making unnecessary copy. We will convert
3229  // sname and file fields in Pkt4 to string one day and life
3230  // will be easier.
3231  response->setFile(reinterpret_cast<const uint8_t*>(filename.c_str()),
3232  filename.size());
3233  found_cnt++;
3234  }
3235  }
3236  }
3237  }
3238 
3239  // Step 3: try to set values using HR. Any values coming from there will override
3240  // the subnet or class values.
3242 }
3243 
3244 OptionPtr
3245 Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
3246  uint32_t netmask = getNetmask4(subnet->get().second).toUint32();
3247 
3249  DHO_SUBNET_MASK, netmask));
3250 
3251  return (opt);
3252 }
3253 
3254 Pkt4Ptr
3256  // server-id is forbidden.
3257  sanityCheck(discover, FORBIDDEN);
3258 
3259  bool drop = false;
3260  Subnet4Ptr subnet = selectSubnet(discover, drop);
3261 
3262  // Stop here if selectSubnet decided to drop the packet
3263  if (drop) {
3264  return (Pkt4Ptr());
3265  }
3266 
3267  Dhcpv4Exchange ex(alloc_engine_, discover, context, subnet, drop);
3268 
3269  // Stop here if Dhcpv4Exchange constructor decided to drop the packet
3270  if (drop) {
3271  return (Pkt4Ptr());
3272  }
3273 
3274  if (MultiThreadingMgr::instance().getMode()) {
3275  // The lease reclamation cannot run at the same time.
3276  ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3277 
3278  assignLease(ex);
3279  } else {
3280  assignLease(ex);
3281  }
3282 
3283  if (!ex.getResponse()) {
3284  // The offer is empty so return it *now*!
3285  return (Pkt4Ptr());
3286  }
3287 
3288  // Adding any other options makes sense only when we got the lease.
3289  if (!ex.getResponse()->getYiaddr().isV4Zero()) {
3290  // If this is global reservation or the subnet doesn't belong to a shared
3291  // network we have already fetched it and evaluated the classes.
3293 
3294  // Required classification
3295  requiredClassify(ex);
3296 
3297  buildCfgOptionList(ex);
3300  // There are a few basic options that we always want to
3301  // include in the response. If client did not request
3302  // them we append them for him.
3303  appendBasicOptions(ex);
3304 
3305  // Set fixed fields (siaddr, sname, filename) if defined in
3306  // the reservation, class or subnet specific configuration.
3307  setFixedFields(ex);
3308 
3309  } else {
3310  // If the server can't offer an address, it drops the packet.
3311  return (Pkt4Ptr());
3312 
3313  }
3314 
3315  // Set the src/dest IP address, port and interface for the outgoing
3316  // packet.
3317  adjustIfaceData(ex);
3318 
3319  appendServerID(ex);
3320 
3321  return (ex.getResponse());
3322 }
3323 
3324 Pkt4Ptr
3326  // Since we cannot distinguish between client states
3327  // we'll make server-id is optional for REQUESTs.
3328  sanityCheck(request, OPTIONAL);
3329 
3330  bool drop = false;
3331  Subnet4Ptr subnet = selectSubnet(request, drop);
3332 
3333  // Stop here if selectSubnet decided to drop the packet
3334  if (drop) {
3335  return (Pkt4Ptr());
3336  }
3337 
3338  Dhcpv4Exchange ex(alloc_engine_, request, context, subnet, drop);
3339 
3340  // Stop here if Dhcpv4Exchange constructor decided to drop the packet
3341  if (drop) {
3342  return (Pkt4Ptr());
3343  }
3344 
3345  // Note that we treat REQUEST message uniformly, regardless if this is a
3346  // first request (requesting for new address), renewing existing address
3347  // or even rebinding.
3348  if (MultiThreadingMgr::instance().getMode()) {
3349  // The lease reclamation cannot run at the same time.
3350  ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3351 
3352  assignLease(ex);
3353  } else {
3354  assignLease(ex);
3355  }
3356 
3357  Pkt4Ptr response = ex.getResponse();
3358  if (!response) {
3359  // The ack is empty so return it *now*!
3360  return (Pkt4Ptr());
3361  } else if (request->inClass("BOOTP")) {
3362  // Put BOOTP responses in the BOOTP class.
3363  response->addClass("BOOTP");
3364  }
3365 
3366  // Adding any other options makes sense only when we got the lease.
3367  if (!response->getYiaddr().isV4Zero()) {
3368  // If this is global reservation or the subnet doesn't belong to a shared
3369  // network we have already fetched it and evaluated the classes.
3371 
3372  // Required classification
3373  requiredClassify(ex);
3374 
3375  buildCfgOptionList(ex);
3378  // There are a few basic options that we always want to
3379  // include in the response. If client did not request
3380  // them we append them for him.
3381  appendBasicOptions(ex);
3382 
3383  // Set fixed fields (siaddr, sname, filename) if defined in
3384  // the reservation, class or subnet specific configuration.
3385  setFixedFields(ex);
3386  }
3387 
3388  // Set the src/dest IP address, port and interface for the outgoing
3389  // packet.
3390  adjustIfaceData(ex);
3391 
3392  appendServerID(ex);
3393 
3394  // Return the pointer to the context, which will be required by the
3395  // leases4_committed callouts.
3396  context = ex.getContext();
3397 
3398  return (ex.getResponse());
3399 }
3400 
3401 void
3403  // Server-id is mandatory in DHCPRELEASE (see table 5, RFC2131)
3404  // but ISC DHCP does not enforce this, so we'll follow suit.
3405  sanityCheck(release, OPTIONAL);
3406 
3407  // Try to find client-id. Note that for the DHCPRELEASE we don't check if the
3408  // match-client-id configuration parameter is disabled because this parameter
3409  // is configured for subnets and we don't select subnet for the DHCPRELEASE.
3410  // Bogus clients usually generate new client identifiers when they first
3411  // connect to the network, so whatever client identifier has been used to
3412  // acquire the lease, the client identifier carried in the DHCPRELEASE is
3413  // likely to be the same and the lease will be correctly identified in the
3414  // lease database. If supplied client identifier differs from the one used
3415  // to acquire the lease then the lease will remain in the database and
3416  // simply expire.
3417  ClientIdPtr client_id;
3418  OptionPtr opt = release->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
3419  if (opt) {
3420  client_id = ClientIdPtr(new ClientId(opt->getData()));
3421  }
3422 
3423  try {
3424  // Do we have a lease for that particular address?
3425  Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getCiaddr());
3426 
3427  if (!lease) {
3428  // No such lease - bogus release
3430  .arg(release->getLabel())
3431  .arg(release->getCiaddr().toText());
3432  return;
3433  }
3434 
3435  if (!lease->belongsToClient(release->getHWAddr(), client_id)) {
3437  .arg(release->getLabel())
3438  .arg(release->getCiaddr().toText());
3439  return;
3440  }
3441 
3442  bool skip = false;
3443 
3444  // Execute all callouts registered for lease4_release
3445  if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_release_)) {
3446  CalloutHandlePtr callout_handle = getCalloutHandle(release);
3447 
3448  // Use the RAII wrapper to make sure that the callout handle state is
3449  // reset when this object goes out of scope. All hook points must do
3450  // it to prevent possible circular dependency between the callout
3451  // handle and its arguments.
3452  ScopedCalloutHandleState callout_handle_state(callout_handle);
3453 
3454  // Enable copying options from the packet within hook library.
3455  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(release);
3456 
3457  // Pass the original packet
3458  callout_handle->setArgument("query4", release);
3459 
3460  // Pass the lease to be updated
3461  callout_handle->setArgument("lease4", lease);
3462 
3463  // Call all installed callouts
3464  HooksManager::callCallouts(Hooks.hook_index_lease4_release_,
3465  *callout_handle);
3466 
3467  // Callouts decided to skip the next processing step. The next
3468  // processing step would be to send the packet, so skip at this
3469  // stage means "drop response".
3470  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3471  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3472  skip = true;
3475  .arg(release->getLabel());
3476  }
3477  }
3478 
3479  // Callout didn't indicate to skip the release process. Let's release
3480  // the lease.
3481  if (!skip) {
3482  // Ok, we've passed all checks. Let's release this address.
3483  bool success = false; // was the removal operation successful?
3484  bool expired = false; // explicitly expired instead of removed?
3485  auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3486 
3487  // Delete lease only if affinity is disabled.
3488  if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3489  expiration_cfg->getHoldReclaimedTime() &&
3490  lease->valid_lft_ != Lease::INFINITY_LFT) {
3491  // Expire the lease.
3492  lease->valid_lft_ = 0;
3494  expired = true;
3495  success = true;
3496  } else {
3497  success = LeaseMgrFactory::instance().deleteLease(lease);
3498  }
3499 
3500  if (success) {
3501  context.reset(new AllocEngine::ClientContext4());
3502  context->old_lease_ = lease;
3503 
3504  // Release successful
3506  .arg(release->getLabel())
3507  .arg(lease->addr_.toText());
3508 
3509  if (expired) {
3511  .arg(release->getLabel())
3512  .arg(lease->addr_.toText());
3513  } else {
3515  .arg(release->getLabel())
3516  .arg(lease->addr_.toText());
3517 
3518  // Need to decrease statistic for assigned addresses.
3519  StatsMgr::instance().addValue(
3520  StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-addresses"),
3521  static_cast<int64_t>(-1));
3522 
3523  // Remove existing DNS entries for the lease, if any.
3524  queueNCR(CHG_REMOVE, lease);
3525  }
3526  } else {
3527  // Release failed
3529  .arg(release->getLabel())
3530  .arg(lease->addr_.toText());
3531  }
3532  }
3533  } catch (const isc::Exception& ex) {
3535  .arg(release->getLabel())
3536  .arg(release->getCiaddr())
3537  .arg(ex.what());
3538  }
3539 }
3540 
3541 void
3543  // Server-id is mandatory in DHCPDECLINE (see table 5, RFC2131)
3544  // but ISC DHCP does not enforce this, so we'll follow suit.
3545  sanityCheck(decline, OPTIONAL);
3546 
3547  // Client is supposed to specify the address being declined in
3548  // Requested IP address option, but must not set its ciaddr.
3549  // (again, see table 5 in RFC2131).
3550 
3551  OptionCustomPtr opt_requested_address = boost::dynamic_pointer_cast<
3552  OptionCustom>(decline->getOption(DHO_DHCP_REQUESTED_ADDRESS));
3553  if (!opt_requested_address) {
3554 
3555  isc_throw(RFCViolation, "Mandatory 'Requested IP address' option missing"
3556  " in DHCPDECLINE sent from " << decline->getLabel());
3557  }
3558  IOAddress addr(opt_requested_address->readAddress());
3559 
3560  // We could also extract client's address from ciaddr, but that's clearly
3561  // against RFC2131.
3562 
3563  // Now we need to check whether this address really belongs to the client
3564  // that attempts to decline it.
3565  const Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(addr);
3566 
3567  if (!lease) {
3568  // Client tried to decline an address, but we don't have a lease for
3569  // that address. Let's ignore it.
3570  //
3571  // We could assume that we're recovering from a mishandled migration
3572  // to a new server and mark the address as declined, but the window of
3573  // opportunity for that to be useful is small and the attack vector
3574  // would be pretty severe.
3576  .arg(addr.toText()).arg(decline->getLabel());
3577  return;
3578  }
3579 
3580  // Get client-id, if available.
3581  OptionPtr opt_clientid = decline->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
3582  ClientIdPtr client_id;
3583  if (opt_clientid) {
3584  client_id.reset(new ClientId(opt_clientid->getData()));
3585  }
3586 
3587  // Check if the client attempted to decline a lease it doesn't own.
3588  if (!lease->belongsToClient(decline->getHWAddr(), client_id)) {
3589 
3590  // Get printable hardware addresses
3591  string client_hw = decline->getHWAddr() ?
3592  decline->getHWAddr()->toText(false) : "(none)";
3593  string lease_hw = lease->hwaddr_ ?
3594  lease->hwaddr_->toText(false) : "(none)";
3595 
3596  // Get printable client-ids
3597  string client_id_txt = client_id ? client_id->toText() : "(none)";
3598  string lease_id_txt = lease->client_id_ ?
3599  lease->client_id_->toText() : "(none)";
3600 
3601  // Print the warning and we're done here.
3603  .arg(addr.toText()).arg(decline->getLabel())
3604  .arg(client_hw).arg(lease_hw).arg(client_id_txt).arg(lease_id_txt);
3605 
3606  return;
3607  }
3608 
3609  // Ok, all is good. The client is reporting its own address. Let's
3610  // process it.
3611  declineLease(lease, decline, context);
3612 }
3613 
3614 void
3615 Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline,
3616  AllocEngine::ClientContext4Ptr& context) {
3617 
3618  // Let's check if there are hooks installed for decline4 hook point.
3619  // If they are, let's pass the lease and client's packet. If the hook
3620  // sets status to drop, we reject this Decline.
3621  if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_decline_)) {
3622  CalloutHandlePtr callout_handle = getCalloutHandle(decline);
3623 
3624  // Use the RAII wrapper to make sure that the callout handle state is
3625  // reset when this object goes out of scope. All hook points must do
3626  // it to prevent possible circular dependency between the callout
3627  // handle and its arguments.
3628  ScopedCalloutHandleState callout_handle_state(callout_handle);
3629 
3630  // Enable copying options from the packet within hook library.
3631  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(decline);
3632 
3633  // Pass the original packet
3634  callout_handle->setArgument("query4", decline);
3635 
3636  // Pass the lease to be updated
3637  callout_handle->setArgument("lease4", lease);
3638 
3639  // Call callouts
3640  HooksManager::callCallouts(Hooks.hook_index_lease4_decline_,
3641  *callout_handle);
3642 
3643  // Check if callouts decided to skip the next processing step.
3644  // If any of them did, we will drop the packet.
3645  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3646  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3648  .arg(decline->getLabel()).arg(lease->addr_.toText());
3649  return;
3650  }
3651  }
3652 
3653  Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
3654 
3655  // @todo: Call hooks.
3656 
3657  // We need to disassociate the lease from the client. Once we move a lease
3658  // to declined state, it is no longer associated with the client in any
3659  // way.
3660  lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
3661 
3662  try {
3664  } catch (const Exception& ex) {
3665  // Update failed.
3667  .arg(decline->getLabel())
3668  .arg(lease->addr_.toText())
3669  .arg(ex.what());
3670  return;
3671  }
3672 
3673  // Remove existing DNS entries for the lease, if any.
3674  // queueNCR will do the necessary checks and will skip the update, if not needed.
3675  queueNCR(CHG_REMOVE, old_values);
3676 
3677  // Bump up the statistics.
3678 
3679  // Per subnet declined addresses counter.
3680  StatsMgr::instance().addValue(
3681  StatsMgr::generateName("subnet", lease->subnet_id_, "declined-addresses"),
3682  static_cast<int64_t>(1));
3683 
3684  // Global declined addresses counter.
3685  StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
3686 
3687  // We do not want to decrease the assigned-addresses at this time. While
3688  // technically a declined address is no longer allocated, the primary usage
3689  // of the assigned-addresses statistic is to monitor pool utilization. Most
3690  // people would forget to include declined-addresses in the calculation,
3691  // and simply do assigned-addresses/total-addresses. This would have a bias
3692  // towards under-representing pool utilization, if we decreased allocated
3693  // immediately after receiving DHCPDECLINE, rather than later when we recover
3694  // the address.
3695 
3696  context.reset(new AllocEngine::ClientContext4());
3697  context->new_lease_ = lease;
3698 
3699  LOG_INFO(lease4_logger, DHCP4_DECLINE_LEASE).arg(lease->addr_.toText())
3700  .arg(decline->getLabel()).arg(lease->valid_lft_);
3701 }
3702 
3703 Pkt4Ptr
3705  // server-id is supposed to be forbidden (as is requested address)
3706  // but ISC DHCP does not enforce either. So neither will we.
3707  sanityCheck(inform, OPTIONAL);
3708 
3709  bool drop = false;
3710  Subnet4Ptr subnet = selectSubnet(inform, drop);
3711 
3712  // Stop here if selectSubnet decided to drop the packet
3713  if (drop) {
3714  return (Pkt4Ptr());
3715  }
3716 
3717  Dhcpv4Exchange ex(alloc_engine_, inform, context, subnet, drop);
3718 
3719  // Stop here if Dhcpv4Exchange constructor decided to drop the packet
3720  if (drop) {
3721  return (Pkt4Ptr());
3722  }
3723 
3724  Pkt4Ptr ack = ex.getResponse();
3725 
3726  // If this is global reservation or the subnet doesn't belong to a shared
3727  // network we have already fetched it and evaluated the classes.
3729 
3730  requiredClassify(ex);
3731 
3732  buildCfgOptionList(ex);
3735  appendBasicOptions(ex);
3736  adjustIfaceData(ex);
3737 
3738  // Set fixed fields (siaddr, sname, filename) if defined in
3739  // the reservation, class or subnet specific configuration.
3740  setFixedFields(ex);
3741 
3742  // There are cases for the DHCPINFORM that the server receives it via
3743  // relay but will send the response to the client's unicast address
3744  // carried in the ciaddr. In this case, the giaddr and hops field should
3745  // be cleared (these fields were copied by the copyDefaultFields function).
3746  // Also Relay Agent Options should be removed if present.
3747  if (ack->getRemoteAddr() != inform->getGiaddr()) {
3749  .arg(inform->getLabel())
3750  .arg(ack->getRemoteAddr())
3751  .arg(ack->getIface());
3752  ack->setHops(0);
3753  ack->setGiaddr(IOAddress::IPV4_ZERO_ADDRESS());
3754  ack->delOption(DHO_DHCP_AGENT_OPTIONS);
3755  }
3756 
3757  // The DHCPACK must contain server id.
3758  appendServerID(ex);
3759 
3760  return (ex.getResponse());
3761 }
3762 
3763 bool
3764 Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
3765  // Check that the message type is accepted by the server. We rely on the
3766  // function called to log a message if needed.
3767  if (!acceptMessageType(query)) {
3768  return (false);
3769  }
3770  // Check if the message from directly connected client (if directly
3771  // connected) should be dropped or processed.
3772  if (!acceptDirectRequest(query)) {
3774  .arg(query->getLabel())
3775  .arg(query->getIface());
3776  return (false);
3777  }
3778 
3779  // Check if the DHCPv4 packet has been sent to us or to someone else.
3780  // If it hasn't been sent to us, drop it!
3781  if (!acceptServerId(query)) {
3783  .arg(query->getLabel())
3784  .arg(query->getIface());
3785  return (false);
3786  }
3787 
3788  return (true);
3789 }
3790 
3791 bool
3793  // Accept all relayed messages.
3794  if (pkt->isRelayed()) {
3795  return (true);
3796  }
3797 
3798  // Accept all DHCPv4-over-DHCPv6 messages.
3799  if (pkt->isDhcp4o6()) {
3800  return (true);
3801  }
3802 
3803  // The source address must not be zero for the DHCPINFORM message from
3804  // the directly connected client because the server will not know where
3805  // to respond if the ciaddr was not present.
3806  try {
3807  if (pkt->getType() == DHCPINFORM) {
3808  if (pkt->getRemoteAddr().isV4Zero() &&
3809  pkt->getCiaddr().isV4Zero()) {
3810  return (false);
3811  }
3812  }
3813  } catch (...) {
3814  // If we got here, it is probably because the message type hasn't
3815  // been set. But, this should not really happen assuming that
3816  // we validate the message type prior to calling this function.
3817  return (false);
3818  }
3819  bool drop = false;
3820  bool result = (!pkt->getLocalAddr().isV4Bcast() ||
3821  selectSubnet(pkt, drop, true));
3822  if (drop) {
3823  // The packet must be dropped but as sanity_only is true it is dead code.
3824  return (false);
3825  }
3826  return (result);
3827 }
3828 
3829 bool
3831  // When receiving a packet without message type option, getType() will
3832  // throw.
3833  int type;
3834  try {
3835  type = query->getType();
3836 
3837  } catch (...) {
3839  .arg(query->getLabel())
3840  .arg(query->getIface());
3841  return (false);
3842  }
3843 
3844  // Once we know that the message type is within a range of defined DHCPv4
3845  // messages, we do a detailed check to make sure that the received message
3846  // is targeted at server. Note that we could have received some Offer
3847  // message broadcasted by the other server to a relay. Even though, the
3848  // server would rather unicast its response to a relay, let's be on the
3849  // safe side. Also, we want to drop other messages which we don't support.
3850  // All these valid messages that we are not going to process are dropped
3851  // silently.
3852 
3853  switch(type) {
3854  case DHCPDISCOVER:
3855  case DHCPREQUEST:
3856  case DHCPRELEASE:
3857  case DHCPDECLINE:
3858  case DHCPINFORM:
3859  return (true);
3860  break;
3861 
3862  case DHCP_NOTYPE:
3864  .arg(query->getLabel());
3865  break;
3866 
3867  default:
3868  // If we receive a message with a non-existing type, we are logging it.
3869  if (type >= DHCP_TYPES_EOF) {
3871  .arg(query->getLabel())
3872  .arg(type);
3873  } else {
3874  // Exists but we don't support it.
3876  .arg(query->getLabel())
3877  .arg(type);
3878  }
3879  break;
3880  }
3881 
3882  return (false);
3883 }
3884 
3885 bool
3886 Dhcpv4Srv::acceptServerId(const Pkt4Ptr& query) const {
3887  // This function is meant to be called internally by the server class, so
3888  // we rely on the caller to sanity check the pointer and we don't check
3889  // it here.
3890 
3891  // Check if server identifier option is present. If it is not present
3892  // we accept the message because it is targeted to all servers.
3893  // Note that we don't check cases that server identifier is mandatory
3894  // but not present. This is meant to be sanity checked in other
3895  // functions.
3896  OptionPtr option = query->getOption(DHO_DHCP_SERVER_IDENTIFIER);
3897  if (!option) {
3898  return (true);
3899  }
3900  // Server identifier is present. Let's convert it to 4-byte address
3901  // and try to match with server identifiers used by the server.
3902  OptionCustomPtr option_custom =
3903  boost::dynamic_pointer_cast<OptionCustom>(option);
3904  // Unable to convert the option to the option type which encapsulates it.
3905  // We treat this as non-matching server id.
3906  if (!option_custom) {
3907  return (false);
3908  }
3909  // The server identifier option should carry exactly one IPv4 address.
3910  // If the option definition for the server identifier doesn't change,
3911  // the OptionCustom object should have exactly one IPv4 address and
3912  // this check is somewhat redundant. On the other hand, if someone
3913  // breaks option it may be better to check that here.
3914  if (option_custom->getDataFieldsNum() != 1) {
3915  return (false);
3916  }
3917 
3918  // The server identifier MUST be an IPv4 address. If given address is
3919  // v6, it is wrong.
3920  IOAddress server_id = option_custom->readAddress();
3921  if (!server_id.isV4()) {
3922  return (false);
3923  }
3924 
3925  // According to RFC5107, the RAI_OPTION_SERVER_ID_OVERRIDE option if
3926  // present, should match DHO_DHCP_SERVER_IDENTIFIER option.
3927  OptionPtr rai_option = query->getOption(DHO_DHCP_AGENT_OPTIONS);
3928  if (rai_option) {
3929  OptionPtr rai_suboption = rai_option->getOption(RAI_OPTION_SERVER_ID_OVERRIDE);
3930  if (rai_suboption && (server_id.toBytes() == rai_suboption->toBinary())) {
3931  return (true);
3932  }
3933  }
3934 
3935  // This function iterates over all interfaces on which the
3936  // server is listening to find the one which has a socket bound
3937  // to the address carried in the server identifier option.
3938  // This has some performance implications. However, given that
3939  // typically there will be just a few active interfaces the
3940  // performance hit should be acceptable. If it turns out to
3941  // be significant, we will have to cache server identifiers
3942  // when sockets are opened.
3943  if (IfaceMgr::instance().hasOpenSocket(server_id)) {
3944  return (true);
3945  }
3946 
3947  // There are some cases when an administrator explicitly sets server
3948  // identifier (option 54) that should be used for a given, subnet,
3949  // network etc. It doesn't have to be an address assigned to any of
3950  // the server interfaces. Thus, we have to check if the server
3951  // identifier received is the one that we explicitly set in the
3952  // server configuration. At this point, we don't know which subnet
3953  // the client belongs to so we can't match the server id with any
3954  // subnet. We simply check if this server identifier is configured
3955  // anywhere. This should be good enough to eliminate exchanges
3956  // with other servers in the same network.
3957 
3965 
3967 
3968  // Check if there is at least one subnet configured with this server
3969  // identifier.
3970  ConstCfgSubnets4Ptr cfg_subnets = cfg->getCfgSubnets4();
3971  if (cfg_subnets->hasSubnetWithServerId(server_id)) {
3972  return (true);
3973  }
3974 
3975  // This server identifier is not configured for any of the subnets, so
3976  // check on the shared network level.
3977  CfgSharedNetworks4Ptr cfg_networks = cfg->getCfgSharedNetworks4();
3978  if (cfg_networks->hasNetworkWithServerId(server_id)) {
3979  return (true);
3980  }
3981 
3982  // Check if the server identifier is configured at client class level.
3983  const ClientClasses& classes = query->getClasses();
3984  for (ClientClasses::const_iterator cclass = classes.cbegin();
3985  cclass != classes.cend(); ++cclass) {
3986  // Find the client class definition for this class
3987  const ClientClassDefPtr& ccdef = CfgMgr::instance().getCurrentCfg()->
3988  getClientClassDictionary()->findClass(*cclass);
3989  if (!ccdef) {
3990  continue;
3991  }
3992 
3993  if (ccdef->getCfgOption()->empty()) {
3994  // Skip classes which don't configure options
3995  continue;
3996  }
3997 
3998  OptionCustomPtr context_opt_server_id = boost::dynamic_pointer_cast<OptionCustom>
3999  (ccdef->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_DHCP_SERVER_IDENTIFIER).option_);
4000  if (context_opt_server_id && (context_opt_server_id->readAddress() == server_id)) {
4001  return (true);
4002  }
4003  }
4004 
4005  // Finally, it is possible that the server identifier is specified
4006  // on the global level.
4007  ConstCfgOptionPtr cfg_global_options = cfg->getCfgOption();
4008  OptionCustomPtr opt_server_id = boost::dynamic_pointer_cast<OptionCustom>
4009  (cfg_global_options->get(DHCP4_OPTION_SPACE, DHO_DHCP_SERVER_IDENTIFIER).option_);
4010 
4011  return (opt_server_id && (opt_server_id->readAddress() == server_id));
4012 }
4013 
4014 void
4016  OptionPtr server_id = query->getOption(DHO_DHCP_SERVER_IDENTIFIER);
4017  switch (serverid) {
4018  case FORBIDDEN:
4019  if (server_id) {
4020  isc_throw(RFCViolation, "Server-id option was not expected, but"
4021  << " received in message "
4022  << query->getName());
4023  }
4024  break;
4025 
4026  case MANDATORY:
4027  if (!server_id) {
4028  isc_throw(RFCViolation, "Server-id option was expected, but not"
4029  " received in message "
4030  << query->getName());
4031  }
4032  break;
4033 
4034  case OPTIONAL:
4035  // do nothing here
4036  ;
4037  }
4038 
4039  // If there is HWAddress set and it is non-empty, then we're good
4040  if (query->getHWAddr() && !query->getHWAddr()->hwaddr_.empty()) {
4041  return;
4042  }
4043 
4044  // There has to be something to uniquely identify the client:
4045  // either non-zero MAC address or client-id option present (or both)
4046  OptionPtr client_id = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
4047 
4048  // If there's no client-id (or a useless one is provided, i.e. 0 length)
4049  if (!client_id || client_id->len() == client_id->getHeaderLen()) {
4050  isc_throw(RFCViolation, "Missing or useless client-id and no HW address"
4051  " provided in message "
4052  << query->getName());
4053  }
4054 }
4055 
4058 }
4059 
4061  // First collect required classes
4062  Pkt4Ptr query = ex.getQuery();
4063  ClientClasses classes = query->getClasses(true);
4064  Subnet4Ptr subnet = ex.getContext()->subnet_;
4065 
4066  if (subnet) {
4067  // Begin by the shared-network
4068  SharedNetwork4Ptr network;
4069  subnet->getSharedNetwork(network);
4070  if (network) {
4071  const ClientClasses& to_add = network->getRequiredClasses();
4072  for (ClientClasses::const_iterator cclass = to_add.cbegin();
4073  cclass != to_add.cend(); ++cclass) {
4074  classes.insert(*cclass);
4075  }
4076  }
4077 
4078  // Followed by the subnet
4079  const ClientClasses& to_add = subnet->getRequiredClasses();
4080  for(ClientClasses::const_iterator cclass = to_add.cbegin();
4081  cclass != to_add.cend(); ++cclass) {
4082  classes.insert(*cclass);
4083  }
4084 
4085  // And finish by the pool
4086  Pkt4Ptr resp = ex.getResponse();
4088  if (resp) {
4089  addr = resp->getYiaddr();
4090  }
4091  if (!addr.isV4Zero()) {
4092  PoolPtr pool = subnet->getPool(Lease::TYPE_V4, addr, false);
4093  if (pool) {
4094  const ClientClasses& to_add = pool->getRequiredClasses();
4095  for (ClientClasses::const_iterator cclass = to_add.cbegin();
4096  cclass != to_add.cend(); ++cclass) {
4097  classes.insert(*cclass);
4098  }
4099  }
4100  }
4101 
4102  // host reservation???
4103  }
4104 
4105  // Run match expressions
4106  // Note getClientClassDictionary() cannot be null
4107  const ClientClassDictionaryPtr& dict =
4108  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4109  for (ClientClasses::const_iterator cclass = classes.cbegin();
4110  cclass != classes.cend(); ++cclass) {
4111  const ClientClassDefPtr class_def = dict->findClass(*cclass);
4112  if (!class_def) {
4114  .arg(*cclass);
4115  continue;
4116  }
4117  const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
4118  // Nothing to do without an expression to evaluate
4119  if (!expr_ptr) {
4121  .arg(*cclass);
4122  continue;
4123  }
4124  // Evaluate the expression which can return false (no match),
4125  // true (match) or raise an exception (error)
4126  try {
4127  bool status = evaluateBool(*expr_ptr, *query);
4128  if (status) {
4130  .arg(*cclass)
4131  .arg(status);
4132  // Matching: add the class
4133  query->addClass(*cclass);
4134  } else {
4136  .arg(*cclass)
4137  .arg(status);
4138  }
4139  } catch (const Exception& ex) {
4141  .arg(*cclass)
4142  .arg(ex.what());
4143  } catch (...) {
4145  .arg(*cclass)
4146  .arg("get exception?");
4147  }
4148  }
4149 }
4150 
4151 void
4153  // Iterate on the list of deferred option codes
4154  BOOST_FOREACH(const uint16_t& code, query->getDeferredOptions()) {
4155  OptionDefinitionPtr def;
4156  // Iterate on client classes
4157  const ClientClasses& classes = query->getClasses();
4158  for (ClientClasses::const_iterator cclass = classes.cbegin();
4159  cclass != classes.cend(); ++cclass) {
4160  // Get the client class definition for this class
4161  const ClientClassDefPtr& ccdef =
4163  getClientClassDictionary()->findClass(*cclass);
4164  // If not found skip it
4165  if (!ccdef) {
4166  continue;
4167  }
4168  // If there is no option definition skip it
4169  if (!ccdef->getCfgOptionDef()) {
4170  continue;
4171  }
4172  def = ccdef->getCfgOptionDef()->get(DHCP4_OPTION_SPACE, code);
4173  // Stop at the first client class with a definition
4174  if (def) {
4175  break;
4176  }
4177  }
4178  // If not found try the global definition
4179  if (!def) {
4181  }
4182  if (!def) {
4184  }
4185  // Finish by last resort definition
4186  if (!def) {
4188  }
4189  // If not defined go to the next option
4190  if (!def) {
4191  continue;
4192  }
4193  // Get the existing option for its content and remove all
4194  OptionPtr opt = query->getOption(code);
4195  if (!opt) {
4196  // should not happen but do not crash anyway
4199  .arg(code);
4200  continue;
4201  }
4202  // Because options have already been fused, the buffer contains entire
4203  // data.
4204  const OptionBuffer buf = opt->getData();
4205  try {
4206  // Unpack the option
4207  opt = def->optionFactory(Option::V4, code, buf);
4208  } catch (const std::exception& e) {
4209  // Failed to parse the option.
4212  .arg(code)
4213  .arg(e.what());
4214  continue;
4215  }
4216  while (query->delOption(code)) {
4217  // continue
4218  }
4219  // Add the unpacked option.
4220  query->addOption(opt);
4221  }
4222 }
4223 
4224 void
4227  if (d2_mgr.ddnsEnabled()) {
4228  // Updates are enabled, so lets start the sender, passing in
4229  // our error handler.
4230  // This may throw so wherever this is called needs to ready.
4231  d2_mgr.startSender(std::bind(&Dhcpv4Srv::d2ClientErrorHandler,
4232  this, ph::_1, ph::_2));
4233  }
4234 }
4235 
4236 void
4239  if (d2_mgr.ddnsEnabled()) {
4240  // Updates are enabled, so lets stop the sender
4241  d2_mgr.stopSender();
4242  }
4243 }
4244 
4245 void
4250  arg(result).arg((ncr ? ncr->toText() : " NULL "));
4251  // We cannot communicate with kea-dhcp-ddns, suspend further updates.
4255 }
4256 
4257 // Refer to config_report so it will be embedded in the binary
4259 
4260 std::string
4261 Dhcpv4Srv::getVersion(bool extended) {
4262  std::stringstream tmp;
4263 
4264  tmp << VERSION;
4265  if (extended) {
4266  tmp << endl << EXTENDED_VERSION << endl;
4267  tmp << "linked with:" << endl;
4268  tmp << Logger::getVersion() << endl;
4269  tmp << CryptoLink::getVersion() << endl;
4270  tmp << "database:" << endl;
4271 #ifdef HAVE_MYSQL
4272  tmp << MySqlLeaseMgr::getDBVersion() << endl;
4273 #endif
4274 #ifdef HAVE_PGSQL
4275  tmp << PgSqlLeaseMgr::getDBVersion() << endl;
4276 #endif
4278 
4279  // @todo: more details about database runtime
4280  }
4281 
4282  return (tmp.str());
4283 }
4284 
4286  // Note that we're not bumping pkt4-received statistic as it was
4287  // increased early in the packet reception code.
4288 
4289  string stat_name = "pkt4-unknown-received";
4290  try {
4291  switch (query->getType()) {
4292  case DHCPDISCOVER:
4293  stat_name = "pkt4-discover-received";
4294  break;
4295  case DHCPOFFER:
4296  // Should not happen, but let's keep a counter for it
4297  stat_name = "pkt4-offer-received";
4298  break;
4299  case DHCPREQUEST:
4300  stat_name = "pkt4-request-received";
4301  break;
4302  case DHCPACK:
4303  // Should not happen, but let's keep a counter for it
4304  stat_name = "pkt4-ack-received";
4305  break;
4306  case DHCPNAK:
4307  // Should not happen, but let's keep a counter for it
4308  stat_name = "pkt4-nak-received";
4309  break;
4310  case DHCPRELEASE:
4311  stat_name = "pkt4-release-received";
4312  break;
4313  case DHCPDECLINE:
4314  stat_name = "pkt4-decline-received";
4315  break;
4316  case DHCPINFORM:
4317  stat_name = "pkt4-inform-received";
4318  break;
4319  default:
4320  ; // do nothing
4321  }
4322  }
4323  catch (...) {
4324  // If the incoming packet doesn't have option 53 (message type)
4325  // or a hook set pkt4_receive_skip, then Pkt4::getType() may
4326  // throw an exception. That's ok, we'll then use the default
4327  // name of pkt4-unknown-received.
4328  }
4329 
4331  static_cast<int64_t>(1));
4332 }
4333 
4334 void Dhcpv4Srv::processStatsSent(const Pkt4Ptr& response) {
4335  // Increase generic counter for sent packets.
4337  static_cast<int64_t>(1));
4338 
4339  // Increase packet type specific counter for packets sent.
4340  string stat_name;
4341  switch (response->getType()) {
4342  case DHCPOFFER:
4343  stat_name = "pkt4-offer-sent";
4344  break;
4345  case DHCPACK:
4346  stat_name = "pkt4-ack-sent";
4347  break;
4348  case DHCPNAK:
4349  stat_name = "pkt4-nak-sent";
4350  break;
4351  default:
4352  // That should never happen
4353  return;
4354  }
4355 
4357  static_cast<int64_t>(1));
4358 }
4359 
4361  return (Hooks.hook_index_buffer4_receive_);
4362 }
4363 
4365  return (Hooks.hook_index_pkt4_receive_);
4366 }
4367 
4369  return (Hooks.hook_index_subnet4_select_);
4370 }
4371 
4373  return (Hooks.hook_index_lease4_release_);
4374 }
4375 
4377  return (Hooks.hook_index_pkt4_send_);
4378 }
4379 
4381  return (Hooks.hook_index_buffer4_send_);
4382 }
4383 
4385  return (Hooks.hook_index_lease4_decline_);
4386 }
4387 
4389  // Dump all of our current packets, anything that is mid-stream
4390  HooksManager::clearParkingLots();
4391 }
4392 
4393 std::list<std::list<std::string>> Dhcpv4Srv::jsonPathsToRedact() const {
4394  static std::list<std::list<std::string>> const list({
4395  {"config-control", "config-databases", "[]"},
4396  {"hooks-libraries", "[]", "parameters", "*"},
4397  {"hosts-database"},
4398  {"hosts-databases", "[]"},
4399  {"lease-database"},
4400  });
4401  return list;
4402 }
4403 
4404 } // namespace dhcp
4405 } // namespace isc
const isc::log::MessageID DHCP4_NCR_CREATION_FAILED
RAII class creating a critical section.
static std::string lifetimeToText(uint32_t lifetime)
Print lifetime.
Definition: lease.cc:29
void setPacketStatisticsDefaults()
This function sets statistics related to DHCPv4 packets processing to their initial values...
Definition: dhcp4_srv.cc:655
static const uint8_t FLAG_N
Bit N.
asiolink::IOAddress ciaddr_
ciaddr from the client&#39;s message.
const isc::log::MessageID DHCP4_DECLINE_FAIL
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
static OptionDefinitionPtr getRuntimeOptionDef(const std::string &space, const uint16_t code)
Returns runtime (non-standard) option definition by space and option code.
Definition: libdhcp++.cc:186
void run_one()
Main server processing step.
Definition: dhcp4_srv.cc:1039
static int getHookIndexBuffer4Send()
Returns the index for "buffer4_send" hook point.
Definition: dhcp4_srv.cc:4380
const isc::log::MessageID DHCP4_DHCP4O6_SUBNET_SELECTED
Option descriptor.
Definition: cfg_option.h:46
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition: classify.cc:55
virtual void updateLease4(const Lease4Ptr &lease4)=0
Updates IPv4 lease.
const isc::log::MessageID DHCP4_CLASS_UNDEFINED
uint32_t getVendorId() const
Returns enterprise identifier.
Definition: option_vendor.h:87
void appendRequestedVendorOptions(Dhcpv4Exchange &ex)
Appends requested vendor options as requested by client.
Definition: dhcp4_srv.cc:1917
const isc::log::MessageID DHCP4_SUBNET_SELECTION_FAILED
virtual ~Dhcpv4Srv()
Destructor. Used during DHCPv4 service shutdown.
Definition: dhcp4_srv.cc:665
asiolink::IOAddress remote_address_
Source address of the message.
const isc::log::MessageID DHCP4_PACKET_NAK_0002
const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_DATA
uint16_t server_port_
UDP port number on which server listens.
Definition: dhcp4_srv.h:1118
void processPacketPktSend(hooks::CalloutHandlePtr &callout_handle, Pkt4Ptr &query, Pkt4Ptr &rsp)
Executes pkt4_send callout.
Definition: dhcp4_srv.cc:1551
Message Type option missing.
Definition: dhcp4.h:233
bool getFlag(const uint8_t flag) const
Checks if the specified flag of the DHCPv4 Client FQDN Option is set.
const isc::log::MessageID DHCP4_PACKET_SEND
void processPacketBufferSend(hooks::CalloutHandlePtr &callout_handle, Pkt4Ptr &rsp)
Executes buffer4_send callout and sends the response.
Definition: dhcp4_srv.cc:1617
static std::string getDBVersion()
Local version of getDBVersion() class method.
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:69
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
const isc::log::MessageID DHCP4_PACKET_SEND_FAIL
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
bool acceptMessageType(const Pkt4Ptr &query) const
Check if received message type is valid for the server to process.
Definition: dhcp4_srv.cc:3830
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
static void setHostIdentifiers(AllocEngine::ClientContext4Ptr context)
Set host identifiers within a context.
Definition: dhcp4_srv.cc:391
static int getHookIndexBuffer4Receive()
Returns the index for "buffer4_receive" hook point.
Definition: dhcp4_srv.cc:4360
bool acceptDirectRequest(const Pkt4Ptr &query) const
Check if a message sent by directly connected client should be accepted or discarded.
Definition: dhcp4_srv.cc:3792
Exception thrown during option unpacking This exception is thrown when an error has occurred...
Definition: option.h:52
DHCPv4 Option class for handling list of IPv4 addresses.
static SubnetSelector initSelector(const Pkt4Ptr &query)
Build selector from a client&#39;s message.
std::vector< isc::asiolink::IOAddress > AddressContainer
Defines a collection of IPv4 addresses.
const isc::log::MessageID DHCP4_PACKET_PACK
void adjustDomainName(const T &fqdn, T &fqdn_resp, const DdnsParams &ddns_params)
Set server FQDN name based on configuration and a given FQDN.
void buildCfgOptionList(Dhcpv4Exchange &ex)
Build the configured option list.
Definition: dhcp4_srv.cc:1734
static void destroy()
Destroy lease manager.
const isc::log::MessageID DHCP4_LEASE_ADVERT
const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES
const isc::log::MessageID DHCP4_RESPONSE_DATA
const isc::log::MessageID DHCP4_PACKET_QUEUE_FULL
An abstract API for lease database.
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:748
const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS
const isc::log::MessageID DHCP4_EMPTY_HOSTNAME
void sendResponseNoThrow(hooks::CalloutHandlePtr &callout_handle, Pkt4Ptr &query, Pkt4Ptr &rsp)
Process an unparked DHCPv4 packet and sends the response.
Definition: dhcp4_srv.cc:1537
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
asiolink::IOAddress option_select_
RAI link select or subnet select option.
const isc::log::MessageID DHCP4_PACKET_DROP_0006
void startSender(D2ClientErrorHandler error_handler, isc::asiolink::IOService &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:476
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
Definition: srv_config.h:1191
const isc::log::MessageID DHCP4_INFORM_DIRECT_REPLY
OptionPtr interface_id_
Interface id option.
boost::shared_ptr< OptionUint8Array > OptionUint8ArrayPtr
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition: lease.h:514
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:477
void initResponse()
Initializes the instance of the response message.
Definition: dhcp4_srv.cc:271
static void classifyPacket(const Pkt4Ptr &pkt)
Assigns incoming packet to zero or more classes.
Definition: dhcp4_srv.cc:566
const isc::log::MessageID DHCP4_TESTING_MODE_SEND_TO_SOURCE_ENABLED
const isc::log::MessageID DHCP4_SHUTDOWN_REQUEST
void adjustRemoteAddr(Dhcpv4Exchange &ex)
Sets remote addresses for outgoing packet.
Definition: dhcp4_srv.cc:3036
boost::shared_ptr< Option4ClientFqdn > Option4ClientFqdnPtr
A pointer to the Option4ClientFqdn object.
Represents a DHCPv6 packet.
Definition: pkt6.h:44
boost::shared_ptr< OptionString > OptionStringPtr
Pointer to the OptionString object.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
void classifyPacket(const Pkt4Ptr &pkt)
Assigns incoming packet to zero or more classes.
Definition: dhcp4_srv.cc:4056
const isc::log::MessageID DHCP4_INIT_REBOOT
isc::log::Logger bad_packet4_logger(DHCP4_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition: dhcp4_log.h:97
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
Holds information about DHCP service enabling status.
Definition: network_state.h:70
const isc::log::MessageID DHCP4_RELEASE_FAIL_WRONG_CLIENT
virtual void sendPacket(const Pkt4Ptr &pkt)
dummy wrapper around IfaceMgr::send()
Definition: dhcp4_srv.cc:921
const isc::log::MessageID DHCP4_POST_ALLOCATION_NAME_UPDATE_FAIL
const isc::log::MessageID DHCP4_SRV_D2STOP_ERROR
STL namespace.
boost::shared_ptr< CfgIface > CfgIfacePtr
A pointer to the CfgIface .
Definition: cfg_iface.h:501
asiolink::IOAddress giaddr_
giaddr from the client&#39;s message.
const isc::log::MessageID DHCP4_DHCP4O6_SUBNET_SELECTION_FAILED
const isc::log::MessageID DHCP4_HOOK_DDNS_UPDATE
Represents DHCPv4 Client FQDN Option (code 81).
const isc::log::MessageID DHCP4_BUFFER_WAIT_SIGNAL
const isc::log::MessageID DHCP4_RELEASE_FAIL
Forward declaration to OptionInt.
const isc::log::MessageID DHCP4_HOOK_BUFFER_RCVD_SKIP
boost::shared_ptr< Pool > PoolPtr
a pointer to either IPv4 or IPv6 Pool
Definition: pool.h:491
std::string getValue() const
Returns the string value held by the option.
static Dhcp4to6Ipc & instance()
Returns pointer to the sole instance of Dhcp4to6Ipc.
Definition: dhcp4to6_ipc.cc:32
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:212
bool earlyGHRLookup(const Pkt4Ptr &query, AllocEngine::ClientContext4Ptr ctx)
Initialize client context and perform early global reservations lookup.
Definition: dhcp4_srv.cc:926
const int DBG_DHCP4_BASIC_DATA
Debug level used to log the traces with some basic data.
Definition: dhcp4_log.h:45
isc::log::Logger options4_logger(DHCP4_OPTIONS_LOGGER_NAME)
Logger for options parser.
Definition: dhcp4_log.h:109
const int DBG_DHCP4_START
Debug level used to log information during server startup.
Definition: dhcp4_log.h:24
Dhcpv4Srv(uint16_t server_port=DHCP4_SERVER_PORT, uint16_t client_port=0, const bool use_bcast=true, const bool direct_response_desired=true)
Default constructor.
Definition: dhcp4_srv.cc:604
int getExitValue()
Fetches the exit value.
Definition: daemon.h:220
void discardPackets()
Discards parked packets Clears the packet parking lots of all packets.
Definition: dhcp4_srv.cc:4388
void setDomainName(const std::string &domain_name, const DomainNameType domain_name_type)
Set new domain-name.
const int DBGLVL_PKT_HANDLING
This debug level is reserved for logging the details of packet handling, such as dropping the packet ...
Definition: log_dbglevels.h:58
const_iterator cbegin() const
Iterators to the first element.
Definition: classify.h:152
void postAllocateNameUpdate(const AllocEngine::ClientContext4Ptr &ctx, const Lease4Ptr &lease, const Pkt4Ptr &query, const Pkt4Ptr &resp, bool client_name_changed)
Update client name and DNS flags in the lease and response.
Definition: dhcp4_srv.cc:2829
boost::shared_ptr< OptionUint32 > OptionUint32Ptr
Definition: option_int.h:35
RAII object enabling copying options retrieved from the packet.
Definition: pkt.h:40
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const isc::log::MessageID DHCP4_FLEX_ID
isc::log::Logger packet4_logger(DHCP4_PACKET_LOGGER_NAME)
Logger for processed packets.
Definition: dhcp4_log.h:103
void createNameChangeRequests(const Lease4Ptr &lease, const Lease4Ptr &old_lease, const DdnsParams &ddns_params)
Creates NameChangeRequests which correspond to the lease which has been acquired. ...
Definition: dhcp4_srv.cc:2446
boost::shared_ptr< AllocEngine > AllocEnginePtr
A pointer to the AllocEngine object.
static int getHookIndexLease4Release()
Returns the index for "lease4_release" hook point.
Definition: dhcp4_srv.cc:4372
const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_MALFORMED
static StatsMgr & instance()
Statistics Manager accessor method.
const int DBG_DHCP4_BASIC
Debug level used to trace basic operations within the code.
Definition: dhcp4_log.h:33
const isc::log::MessageID DHCP4_DHCP4O6_SUBNET_DATA
boost::shared_ptr< const CfgSubnets4 > ConstCfgSubnets4Ptr
Const pointer.
Definition: cfg_subnets4.h:348
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
void processRelease(Pkt4Ptr &release, AllocEngine::ClientContext4Ptr &context)
Processes incoming DHCPRELEASE messages.
Definition: dhcp4_srv.cc:3402
Statistics Manager class.
ClientClasses client_classes_
Classes that the client belongs to.
static const size_t OPTION4_HDR_LEN
length of the usual DHCPv4 option header (there are exceptions)
Definition: option.h:77
const isc::log::MessageID DHCP4_DECLINE_LEASE
const isc::log::MessageID DHCP4_HOOK_BUFFER_RCVD_DROP
Server uses routing to determine the right interface to send response.
Definition: cfg_iface.h:148
Pkt6Ptr getPkt6() const
Returns encapsulating DHCPv6 message.
Definition: pkt4o6.h:47
const isc::log::MessageID DHCP4_PACKET_DROP_0008
const isc::log::MessageID DHCP4_DECLINE_LEASE_MISMATCH
asiolink::IOAddress first_relay_linkaddr_
First relay link address.
const isc::log::MessageID DHCP4_PACKET_PACK_FAIL
void setFixedFields(Dhcpv4Exchange &ex)
Sets fixed fields of the outgoing packet.
Definition: dhcp4_srv.cc:3137
bool send(const Pkt6Ptr &pkt)
Sends an IPv6 packet.
Definition: iface_mgr.cc:1126
void setMatchingPacketFilter(const bool direct_response_desired=false)
Set Packet Filter object to handle send/receive packets.
Subnet selector used to specify parameters used to select a subnet.
OptionInt< uint32_t > OptionUint32
Definition: option_int.h:34
isc::log::Logger dhcp4_logger(DHCP4_APP_LOGGER_NAME)
Base logger for DHCPv4 server.
Definition: dhcp4_log.h:90
static std::string getDBVersion()
Class method to return extended version info This class method must be redeclared and redefined in de...
Definition: lease_mgr.cc:360
const isc::log::MessageID DHCP4_RELEASE_EXCEPTION
#define DOCSIS3_V4_ORO
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
boost::shared_ptr< CfgSharedNetworks4 > CfgSharedNetworks4Ptr
Pointer to the configuration of IPv4 shared networks.
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
ClientClassContainer::const_iterator const_iterator
Type of iterators.
Definition: classify.h:112
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition: dhcp4_srv.h:304
Pkt4Ptr processInform(Pkt4Ptr &inform, AllocEngine::ClientContext4Ptr &context)
Processes incoming DHCPINFORM messages.
Definition: dhcp4_srv.cc:3704
Definition: edns.h:19
const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED
virtual void d2ClientErrorHandler(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Implements the error handler for DHCP_DDNS IO errors.
Definition: dhcp4_srv.cc:4246
structure that describes a single relay information
Definition: pkt6.h:85
const isc::log::MessageID DHCP4_SRV_UNLOAD_LIBRARIES_ERROR
Forward declaration to OptionIntArray.
void processDecline(Pkt4Ptr &decline, AllocEngine::ClientContext4Ptr &context)
Process incoming DHCPDECLINE messages.
Definition: dhcp4_srv.cc:3542
void setTeeTimes(const Lease4Ptr &lease, const Subnet4Ptr &subnet, Pkt4Ptr resp)
Adds the T1 and T2 timers to the outbound response as appropriate.
Definition: dhcp4_srv.cc:2896
const isc::log::MessageID DHCP4_BUFFER_RECEIVE_FAIL
bool acceptServerId(const Pkt4Ptr &pkt) const
Verifies if the server id belongs to our server.
Definition: dhcp4_srv.cc:3886
std::list< ConstCfgOptionPtr > CfgOptionList
Const pointer list.
Definition: cfg_option.h:751
volatile bool shutdown_
Indicates if shutdown is in progress.
Definition: dhcp4_srv.h:1125
static const uint8_t FLAG_S
Bit S.
Implementation of the mechanisms to control the use of the Configuration Backends by the DHCPv4 serve...
Definition: cb_ctl_dhcp4.h:26
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:294
static void sanityCheck(const Pkt4Ptr &query, RequirementLevel serverid)
Verifies if specified packet meets RFC requirements.
Definition: dhcp4_srv.cc:4015
void processDhcp4QueryAndSendResponse(Pkt4Ptr &query, Pkt4Ptr &rsp, bool allow_packet_park)
Process a single incoming DHCPv4 query.
Definition: dhcp4_srv.cc:1310
static void appendServerID(Dhcpv4Exchange &ex)
Adds server identifier option to the server&#39;s response.
Definition: dhcp4_srv.cc:1710
const isc::log::MessageID DHCP4_PACKET_DROP_0010
bool getEnableUpdates() const
Returns whether or not DHCP DDNS updating is enabled.
Definition: srv_config.cc:907
std::string getIdentifierAsText() const
Returns host identifier in a textual form.
Definition: host.cc:270
Pkt4Ptr processRequest(Pkt4Ptr &request, AllocEngine::ClientContext4Ptr &context)
Processes incoming REQUEST and returns REPLY response.
Definition: dhcp4_srv.cc:3325
void processPacket(Pkt4Ptr &query, Pkt4Ptr &rsp, bool allow_packet_park=true)
Process a single incoming DHCPv4 packet.
Definition: dhcp4_srv.cc:1135
void appendRequestedOptions(Dhcpv4Exchange &ex)
Appends options requested by client.
Definition: dhcp4_srv.cc:1809
boost::shared_ptr< Lease4Collection > Lease4CollectionPtr
A shared pointer to the collection of IPv4 leases.
Definition: lease.h:517
boost::shared_ptr< ClientContext4 > ClientContext4Ptr
Pointer to the ClientContext4.
static int getHookIndexLease4Decline()
Returns the index for "lease4_decline" hook point.
Definition: dhcp4_srv.cc:4384
virtual std::string toText(int indent=0) const
Returns string representation of the option.
static std::string getDBVersion()
Local version of getDBVersion() class method.
bool hasOpenSocket(const uint16_t family) const
Checks if there is at least one socket of the specified family open.
Definition: iface_mgr.cc:431
std::pair< OptionContainerPersistIndex::const_iterator, OptionContainerPersistIndex::const_iterator > OptionContainerPersistRange
Pair of iterators to represent the range of options having the same persistency flag.
Definition: cfg_option.h:290
static int getHookIndexSubnet4Select()
Returns the index for "subnet4_select" hook point.
Definition: dhcp4_srv.cc:4368
static int getHookIndexPkt4Send()
Returns the index for "pkt4_send" hook point.
Definition: dhcp4_srv.cc:4376
const isc::log::MessageID DHCP4_PACKET_DROP_0002
const int DBG_DHCP4_DETAIL_DATA
This level is used to log the contents of packets received and sent.
Definition: dhcp4_log.h:56
A generic exception that is thrown when an unexpected error condition occurs.
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
const isc::log::MessageID DHCP4_LEASE_REUSE
const isc::log::MessageID DHCP4_PACKET_DROP_0013
Wrapper class around callout handle which automatically resets handle&#39;s state.
void stopD2()
Stops DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp4_srv.cc:4237
IPv4 lease.
Definition: lease.h:50
const isc::log::MessageID DHCP4_PACKET_DROP_0004
const isc::log::MessageID DHCP4_NO_LEASE_INIT_REBOOT
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
asiolink::IOAddress local_address_
Address on which the message was received.
const isc::log::MessageID DHCP4_PACKET_RECEIVED
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:547
Flexible host identifier.
Definition: host.h:312
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:804
std::string getDomainName() const
Returns the domain-name in the text format.
const isc::log::MessageID DHCP4_BUFFER_UNPACK
const char *const * dhcp4_config_report
Definition: dhcp4_srv.cc:4258
const isc::log::MessageID DHCP4_HOOK_SUBNET4_SELECT_DROP
const isc::log::MessageID DHCP4_BUFFER_RECEIVED
void processDhcp4Query(Pkt4Ptr &query, Pkt4Ptr &rsp, bool allow_packet_park)
Process a single incoming DHCPv4 query.
Definition: dhcp4_srv.cc:1329
const isc::log::MessageID DHCP4_HOOK_LEASES4_PARKING_LOT_FULL
const isc::log::MessageID DHCP4_CLASS_UNTESTABLE
static void classifyByVendor(const Pkt4Ptr &pkt)
Assign class using vendor-class-identifier option.
Definition: dhcp4_srv.cc:554
const isc::log::MessageID DHCP4_RELEASE_FAIL_NO_LEASE
static void removeDependentEvaluatedClasses(const Pkt4Ptr &query)
Removed evaluated client classes.
Definition: dhcp4_srv.cc:492
const int DBG_DHCP4_DETAIL
Debug level used to trace detailed errors.
Definition: dhcp4_log.h:53
const isc::log::MessageID DHCP4_RELEASE_DELETED
const isc::log::MessageID DHCP4_SRV_DHCP4O6_ERROR
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition: duid.h:103
const isc::log::MessageID DHCP4_PACKET_DROP_0005
const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_DROP
const isc::log::MessageID DHCP4_PACKET_DROP_0001
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition: srv_config.h:47
const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_SKIP
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition: libdhcp++.cc:123
bool empty() const
Check if classes is empty.
Definition: classify.h:138
const int DBG_DHCP4_HOOKS
Debug level used to trace hook related operations.
Definition: dhcp4_log.h:36
const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
OptionPtr option_
Option instance.
Definition: cfg_option.h:49
virtual Pkt4Ptr receivePacket(int timeout)
dummy wrapper around IfaceMgr::receive4
Definition: dhcp4_srv.cc:916
const isc::log::MessageID DHCP4_RESPONSE_HOSTNAME_DATA
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
const isc::log::MessageID DHCP4_GENERATE_FQDN
const isc::log::MessageID DHCP4_HOOK_PACKET_RCVD_SKIP
void processPacketAndSendResponse(Pkt4Ptr &query)
Process a single incoming DHCPv4 packet and sends the response.
Definition: dhcp4_srv.cc:1123
bool evaluateBool(const Expression &expr, Pkt &pkt)
Evaluate a RPN expression for a v4 or v6 packet and return a true or false decision.
Definition: evaluate.cc:14
const isc::log::MessageID DHCP4_OPEN_SOCKET
static void processStatsSent(const Pkt4Ptr &response)
Updates statistics for transmitted packets.
Definition: dhcp4_srv.cc:4334
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
const isc::log::MessageID DHCP4_HOOK_BUFFER_SEND_SKIP
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition: dhcp4_srv.h:1131
Client race avoidance RAII handler.
const isc::log::MessageID DHCP4_PACKET_DROP_0009
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-lfc.
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition: dhcp4_srv.h:1138
Datagram socket, i.e. IP/UDP socket.
Definition: cfg_iface.h:139
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp4_srv.cc:4225
const isc::log::MessageID DHCP4_SUBNET_DATA
void declineLease(const Lease4Ptr &lease, const Pkt4Ptr &decline, AllocEngine::ClientContext4Ptr &context)
Marks lease as declined.
Definition: dhcp4_srv.cc:3615
const char *const config_report[]
Represents DHCPv4 packet.
Definition: pkt4.h:37
const isc::log::MessageID DHCP4_RELEASE_EXPIRED
D2ClientMgr isolates Kea from the details of being a D2 client.
Definition: d2_client_mgr.h:79
const isc::log::MessageID DHCP4_CLASS_ASSIGNED
void getUpdateDirections(const T &fqdn_resp, bool &forward, bool &reverse)
Get directional update flags based on server FQDN flags.
static const uint8_t FLAG_E
Bit E.
bool getSendResponsesToSource() const
Returns value of the test_send_responses_to_source_ flag.
Definition: dhcp4_srv.h:464
RequirementLevel
defines if certain option may, must or must not appear
Definition: dhcp4_srv.h:262
ReplaceClientNameMode
Defines the client name replacement modes.
Definition: d2_client_cfg.h:76
const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_PROCESS
boost::shared_ptr< ClientClassDefList > ClientClassDefListPtr
Defines a pointer to a ClientClassDefList.
const isc::log::MessageID DHCP4_RELEASE
isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr &query, bool &drop, bool sanity_only=false) const
Selects a subnet for a given client&#39;s packet.
Definition: dhcp4_srv.cc:711
const isc::log::MessageID DHCP4_PACKET_PROCESS_EXCEPTION
Exception thrown when a call to select is interrupted by a signal.
Definition: iface_mgr.h:55
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
Dhcp4Hooks Hooks
Definition: dhcp4_srv.cc:147
static std::string srvidToString(const OptionPtr &opt)
converts server-id to text Converts content of server-id option to a text representation, e.g.
Definition: dhcp4_srv.cc:1690
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition: hooks_log.h:37
const isc::log::MessageID DHCP4_HOOK_LEASE4_RELEASE_SKIP
const isc::log::MessageID DHCP4_QUERY_DATA
std::string generateFqdn(const asiolink::IOAddress &address, const DdnsParams &ddns_params, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
const isc::log::MessageID DHCP4_PACKET_DROP_0007
void requiredClassify(Dhcpv4Exchange &ex)
Assigns incoming packet to zero or more classes (required pass).
Definition: dhcp4_srv.cc:4060
Pkt4Ptr getQuery() const
Returns the pointer to the query from the client.
Definition: dhcp4_srv.h:96
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire)
Definition: lease.h:34
const isc::log::MessageID DHCP4_SUBNET_SELECTED
boost::shared_ptr< Pkt4o6 > Pkt4o6Ptr
A pointer to Pkt4o6 object.
Definition: pkt4o6.h:82
void assignLease(Dhcpv4Exchange &ex)
Assigns a lease and appends corresponding options.
Definition: dhcp4_srv.cc:2473
static const std::string VENDOR_CLASS_PREFIX
this is a prefix added to the content of vendor-class option
Definition: dhcp4_srv.h:822
Pkt4Ptr processDiscover(Pkt4Ptr &discover, AllocEngine::ClientContext4Ptr &context)
Processes incoming DISCOVER and returns response.
Definition: dhcp4_srv.cc:3255
const isc::log::MessageID DHCP4_PACKET_PROCESS_STD_EXCEPTION
void closeSockets()
Closes all open sockets.
Definition: iface_mgr.cc:287
Holds Client identifier or client IPv4 address.
Definition: duid.h:111
isc::dhcp::Subnet4Ptr selectSubnet4o6(const Pkt4Ptr &query, bool &drop, bool sanity_only=false) const
Selects a subnet for a given client&#39;s DHCP4o6 packet.
Definition: dhcp4_srv.cc:797
const isc::log::MessageID DHCP4_PACKET_OPTIONS_SKIPPED
bool accept(const Pkt4Ptr &query) const
Checks whether received message should be processed or discarded.
Definition: dhcp4_srv.cc:3764
const isc::log::MessageID DHCP4_SRV_CONSTRUCT_ERROR
void initResponse4o6()
Initializes the DHCPv6 part of the response message.
Definition: dhcp4_srv.cc:297
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
#define DHCP4_OPTION_SPACE
global std option spaces
const isc::log::MessageID DHCP4_DHCP4O6_HOOK_SUBNET4_SELECT_SKIP
const isc::log::MessageID DHCP4_RESPONSE_FQDN_DATA
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:276
const isc::log::MessageID DHCP4_UNKNOWN_ADDRESS_REQUESTED
const_iterator cend() const
Iterators to the past the end element.
Definition: classify.h:165
const isc::log::MessageID DHCP4_LEASE_ALLOC
bool tryLock(Pkt4Ptr query, ContinuationPtr cont=ContinuationPtr())
Tries to acquires a client.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
void deleteResponse()
Removes the response message by resetting the pointer to NULL.
Definition: dhcp4_srv.h:108
const isc::log::MessageID DHCP4_HOOK_SUBNET4_SELECT_SKIP
void appendBasicOptions(Dhcpv4Exchange &ex)
Append basic options if they are not present.
Definition: dhcp4_srv.cc:2057
std::string iface_name_
Name of the interface on which the message was received.
void processClientName(Dhcpv4Exchange &ex)
Processes Client FQDN and Hostname Options sent by a client.
Definition: dhcp4_srv.cc:2098
int run()
Main server processing loop.
Definition: dhcp4_srv.cc:999
CfgOptionList & getCfgOptionList()
Returns the configured option list (non-const version)
Definition: dhcp4_srv.h:118
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition: libdhcp++.cc:244
isc::log::Logger ddns4_logger(DHCP4_DDNS_LOGGER_NAME)
Logger for Hostname or FQDN processing.
Definition: dhcp4_log.h:115
void conditionallySetReservedClientClasses()
Assigns classes retrieved from host reservation database if they haven&#39;t been yet set...
Definition: dhcp4_srv.cc:518
static uint16_t checkRelayPort(const Dhcpv4Exchange &ex)
Check if the relay port RAI sub-option was set in the query.
Definition: dhcp4_srv.cc:2935
const isc::log::MessageID DHCP4_PACKET_NAK_0004
uint16_t getSocket(const isc::dhcp::Pkt6Ptr &pkt)
Return most suitable socket for transmitting specified IPv6 packet.
Definition: iface_mgr.cc:1880
bool getUpdateOnRenew() const
Returns whether or not DNS should be updated when leases renew.
Definition: srv_config.cc:997
boost::shared_ptr< const CfgHostOperations > ConstCfgHostOperationsPtr
Pointer to the const object.
const isc::log::MessageID DHCP4_DHCP4O6_HOOK_SUBNET4_SELECT_DROP
void close()
Close communication socket.
Definition: dhcp4o6_ipc.cc:118
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
const isc::log::MessageID DHCP4_DEFERRED_OPTION_MISSING
void suspendUpdates()
Suspends sending requests.
bool dhcp4o6_
Specifies if the packet is DHCP4o6.
Defines the Dhcp4o6Ipc class.
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const =0
Returns an IPv4 lease for specified IPv4 address.
void setReservedMessageFields()
Sets reserved values of siaddr, sname and file in the server&#39;s response.
Definition: dhcp4_srv.cc:532
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition: cfgmgr.cc:66
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
virtual bool deleteLease(const Lease4Ptr &lease)=0
Deletes an IPv4 lease.
const isc::log::MessageID DHCP4_DEFERRED_OPTION_UNPACK_FAIL
const isc::log::MessageID DHCP4_PACKET_NAK_0001
bool isDirectResponseSupported() const
Check if packet be sent directly to the client having no address.
Definition: iface_mgr.cc:320
Read mutex RAII handler.
bool isClientClassBuiltIn(const ClientClass &client_class)
Check if a client class name is builtin.
isc::log::Logger lease4_logger(DHCP4_LEASE_LOGGER_NAME)
Logger for lease allocation logic.
Definition: dhcp4_log.h:120
DHCPv4 message exchange.
Definition: dhcp4_srv.h:62
void insert(const ClientClass &class_name)
Insert an element.
Definition: classify.h:128
std::list< std::list< std::string > > jsonPathsToRedact() const final override
Return a list of all paths that contain passwords or secrets for kea-dhcp4.
Definition: dhcp4_srv.cc:4393
boost::shared_ptr< StringSanitizer > StringSanitizerPtr
Type representing the pointer to the StringSanitizer.
Definition: strutil.h:359
void shutdown() override
Instructs the server to shut down.
Definition: dhcp4_srv.cc:705
IdentifierType
Type of the host identifier.
Definition: host.h:307
static unsigned int getLabelCount(const std::string &text_name)
Return the number of labels in the Name.
const isc::log::MessageID DHCP4_RESPONSE_HOSTNAME_GENERATE
static LeaseMgr & instance()
Return current lease manager.
std::string qualifyName(const std::string &partial_name, const DdnsParams &ddns_params, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
static const uint16_t FLAG_BROADCAST_MASK
Mask for the value of flags field in the DHCPv4 message to check whether client requested broadcast r...
Definition: pkt4.h:54
void deferredUnpack(Pkt4Ptr &query)
Perform deferred option unpacking.
Definition: dhcp4_srv.cc:4152
isc::hooks::CalloutHandlePtr getCalloutHandle(const T &pktptr)
CalloutHandle Store.
const isc::log::MessageID DHCP4_CLIENT_NAME_PROC_FAIL
const isc::log::MessageID EVAL_RESULT
Definition: eval_messages.h:55
Option with defined data fields represented as buffers that can be accessed using data field index...
Definition: option_custom.h:32
boost::shared_ptr< OptionVendor > OptionVendorPtr
Pointer to a vendor option.
const isc::log::MessageID DHCP4_DECLINE_LEASE_NOT_FOUND
const isc::log::MessageID DHCP4_SUBNET_DYNAMICALLY_CHANGED
const isc::log::MessageID DHCP4_PACKET_NAK_0003
static void evaluateClasses(const Pkt4Ptr &pkt, bool depend_on_known)
Evaluate classes.
Definition: dhcp4_srv.cc:577
static void setReservedClientClasses(AllocEngine::ClientContext4Ptr context)
Assigns classes retrieved from host reservation database.
Definition: dhcp4_srv.cc:507
static std::string getVersion(bool extended)
returns Kea version on stdout and exit.
Definition: dhcp4_srv.cc:4261
uint16_t client_port_
UDP port number to which server sends all responses.
Definition: dhcp4_srv.h:1121
Context information for the DHCPv4 lease allocation.
void processPacketAndSendResponseNoThrow(Pkt4Ptr &query)
Process a single incoming DHCPv4 packet and sends the response.
Definition: dhcp4_srv.cc:1111
void adjustIfaceData(Dhcpv4Exchange &ex)
Set IP/UDP and interface parameters for the DHCPv4 response.
Definition: dhcp4_srv.cc:2948
const isc::log::MessageID DHCP4_HOOK_LEASES4_COMMITTED_PARK
DHCPv4 and DHCPv6 allocation engine.
Definition: alloc_engine.h:47
static void processStatsReceived(const Pkt4Ptr &query)
Class methods for DHCPv4-over-DHCPv6 handler.
Definition: dhcp4_srv.cc:4285
OptionContainer::nth_index< 2 >::type OptionContainerPersistIndex
Type of the index #2 - option persistency flag.
Definition: cfg_option.h:285
const isc::log::MessageID DHCP4_HOOK_LEASES4_COMMITTED_DROP
const isc::log::MessageID DHCP4_PACKET_DROP_0003
boost::shared_ptr< Continuation > ContinuationPtr
Define the type of shared pointers to continuations.
#define VENDOR_ID_CABLE_LABS
Class which represents an option carrying a single string value.
Definition: option_string.h:28
Pkt4Ptr getResponse() const
Returns the pointer to the server&#39;s response.
Definition: dhcp4_srv.h:103
Container for storing client class names.
Definition: classify.h:108
Represents DHCPv4-over-DHCPv6 packet.
Definition: pkt4o6.h:30
const isc::log::MessageID DHCP4_HOOK_DECLINE_SKIP
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
Contains declarations for loggers used by the DHCPv4 server component.
Configuration Manager.
Definition: cfgmgr.h:70
This class represents vendor-specific information option.
Definition: option_vendor.h:30
void setFlag(const uint8_t flag, const bool set)
Modifies the value of the specified DHCPv4 Client Fqdn Option flag.
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
static int getHookIndexPkt4Receive()
Returns the index for "pkt4_receive" hook point.
Definition: dhcp4_srv.cc:4364
void adjustFqdnFlags(const T &fqdn, T &fqdn_resp, const DdnsParams &ddns_params)
Set server FQDN flags based on configuration and a given FQDN.
AllocEngine::ClientContext4Ptr getContext() const
Returns the copy of the context for the Allocation engine.
Definition: dhcp4_srv.h:113
void setValue(const std::string &value)
Sets the string value to be held by the option.
const isc::log::MessageID DHCP4_DDNS_REQUEST_SEND_FAILED
const isc::log::MessageID DHCP4_PACKET_DROP_0014
const isc::log::MessageID DHCP4_RESERVED_HOSTNAME_ASSIGNED
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
ContinuationPtr makeContinuation(Continuation &&cont)
Continuation factory.