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