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