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