Kea  1.5.0
dhcp4_srv.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 #include <kea_version.h>
9 
10 #include <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/dhcp4to6_ipc.h>
26 #include <dhcp4/dhcp4_log.h>
27 #include <dhcp4/dhcp4_srv.h>
29 #include <dhcpsrv/cfgmgr.h>
31 #include <dhcpsrv/cfg_iface.h>
33 #include <dhcpsrv/cfg_subnets4.h>
34 #include <dhcpsrv/lease_mgr.h>
36 #include <dhcpsrv/ncr_generator.h>
37 #include <dhcpsrv/shared_network.h>
38 #include <dhcpsrv/subnet.h>
40 #include <dhcpsrv/utils.h>
41 #include <dhcpsrv/utils.h>
42 #include <eval/evaluate.h>
43 #include <eval/eval_messages.h>
44 #include <hooks/callout_handle.h>
45 #include <hooks/hooks_log.h>
46 #include <hooks/hooks_manager.h>
47 #include <stats/stats_mgr.h>
48 #include <util/strutil.h>
49 #include <stats/stats_mgr.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
60 #ifdef HAVE_CQL
61 #include <dhcpsrv/cql_lease_mgr.h>
62 #endif
64 
65 #include <boost/algorithm/string.hpp>
66 #include <boost/bind.hpp>
67 #include <boost/foreach.hpp>
68 #include <boost/pointer_cast.hpp>
69 #include <boost/shared_ptr.hpp>
70 
71 #include <iomanip>
72 
73 using namespace isc;
74 using namespace isc::asiolink;
75 using namespace isc::cryptolink;
76 using namespace isc::dhcp;
77 using namespace isc::dhcp_ddns;
78 using namespace isc::hooks;
79 using namespace isc::log;
80 using namespace isc::stats;
81 using namespace std;
82 
83 namespace {
84 
86 struct Dhcp4Hooks {
87  int hook_index_buffer4_receive_;
88  int hook_index_pkt4_receive_;
89  int hook_index_subnet4_select_;
90  int hook_index_leases4_committed_;
91  int hook_index_lease4_release_;
92  int hook_index_pkt4_send_;
93  int hook_index_buffer4_send_;
94  int hook_index_lease4_decline_;
95  int hook_index_host4_identifier_;
96 
98  Dhcp4Hooks() {
99  hook_index_buffer4_receive_ = HooksManager::registerHook("buffer4_receive");
100  hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive");
101  hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
102  hook_index_leases4_committed_ = HooksManager::registerHook("leases4_committed");
103  hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send");
104  hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
105  hook_index_buffer4_send_ = HooksManager::registerHook("buffer4_send");
106  hook_index_lease4_decline_ = HooksManager::registerHook("lease4_decline");
107  hook_index_host4_identifier_ = HooksManager::registerHook("host4_identifier");
108  }
109 };
110 
111 } // end of anonymous namespace
112 
113 // Declare a Hooks object. As this is outside any function or method, it
114 // will be instantiated (and the constructor run) when the module is loaded.
115 // As a result, the hook indexes will be defined before any method in this
116 // module is called.
117 Dhcp4Hooks Hooks;
118 
119 
120 namespace isc {
121 namespace dhcp {
122 
123 Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine,
124  const Pkt4Ptr& query,
125  const Subnet4Ptr& subnet)
126  : alloc_engine_(alloc_engine), query_(query), resp_(),
127  context_(new AllocEngine::ClientContext4()) {
128 
129  if (!alloc_engine_) {
130  isc_throw(BadValue, "alloc_engine value must not be NULL"
131  " when creating an instance of the Dhcpv4Exchange");
132  }
133 
134  if (!query_) {
135  isc_throw(BadValue, "query value must not be NULL when"
136  " creating an instance of the Dhcpv4Exchange");
137  }
138 
139  // Create response message.
140  initResponse();
141  // Select subnet for the query message.
142  context_->subnet_ = subnet;
143  // Hardware address.
144  context_->hwaddr_ = query->getHWAddr();
145  // Pointer to client's query.
146  context_->query_ = query;
147 
148  // If subnet found, retrieve client identifier which will be needed
149  // for allocations and search for reservations associated with a
150  // subnet/shared network.
151  if (subnet) {
152  OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
153  if (opt_clientid) {
154  context_->clientid_.reset(new ClientId(opt_clientid->getData()));
155  }
156 
157  // Find static reservations if not disabled for our subnet.
158  if (subnet->getHostReservationMode() != Network::HR_DISABLED) {
159  // Before we can check for static reservations, we need to prepare a set
160  // of identifiers to be used for this.
161  setHostIdentifiers();
162 
163  // Check for static reservations.
164  alloc_engine->findReservation(*context_);
165  }
166  }
167 
168  // Set KNOWN builtin class if something was found, UNKNOWN if not.
169  if (!context_->hosts_.empty()) {
170  query->addClass("KNOWN");
171  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
172  .arg(query->getLabel())
173  .arg("KNOWN");
174  } else {
175  query->addClass("UNKNOWN");
176  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
177  .arg(query->getLabel())
178  .arg("UNKNOWN");
179  }
180 
181  // Perform second pass of classification.
182  Dhcpv4Srv::evaluateClasses(query, true);
183 
184  const ClientClasses& classes = query_->getClasses();
185  if (!classes.empty()) {
186  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
187  .arg(query_->getLabel())
188  .arg(classes.toText());
189  }
190 };
191 
192 void
194  uint8_t resp_type = 0;
195  switch (getQuery()->getType()) {
196  case DHCPDISCOVER:
197  resp_type = DHCPOFFER;
198  break;
199  case DHCPREQUEST:
200  case DHCPINFORM:
201  resp_type = DHCPACK;
202  break;
203  default:
204  ;
205  }
206  // Only create a response if one is required.
207  if (resp_type > 0) {
208  resp_.reset(new Pkt4(resp_type, getQuery()->getTransid()));
209  copyDefaultFields();
210  copyDefaultOptions();
211 
212  if (getQuery()->isDhcp4o6()) {
213  initResponse4o6();
214  }
215  }
216 }
217 
218 void
220  Pkt4o6Ptr query = boost::dynamic_pointer_cast<Pkt4o6>(getQuery());
221  if (!query) {
222  return;
223  }
224  const Pkt6Ptr& query6 = query->getPkt6();
225  Pkt6Ptr resp6(new Pkt6(DHCPV6_DHCPV4_RESPONSE, query6->getTransid()));
226  // Don't add client-id or server-id
227  // But copy relay info
228  if (!query6->relay_info_.empty()) {
229  resp6->copyRelayInfo(query6);
230  }
231  // Copy interface, and remote address and port
232  resp6->setIface(query6->getIface());
233  resp6->setIndex(query6->getIndex());
234  resp6->setRemoteAddr(query6->getRemoteAddr());
235  resp6->setRemotePort(query6->getRemotePort());
236  resp_.reset(new Pkt4o6(resp_, resp6));
237 }
238 
239 void
240 Dhcpv4Exchange::copyDefaultFields() {
241  resp_->setIface(query_->getIface());
242  resp_->setIndex(query_->getIndex());
243 
244  // explicitly set this to 0
245  resp_->setSiaddr(IOAddress::IPV4_ZERO_ADDRESS());
246  // ciaddr is always 0, except for the Renew/Rebind state when it may
247  // be set to the ciaddr sent by the client.
248  resp_->setCiaddr(IOAddress::IPV4_ZERO_ADDRESS());
249  resp_->setHops(query_->getHops());
250 
251  // copy MAC address
252  resp_->setHWAddr(query_->getHWAddr());
253 
254  // relay address
255  resp_->setGiaddr(query_->getGiaddr());
256 
257  // If src/dest HW addresses are used by the packet filtering class
258  // we need to copy them as well. There is a need to check that the
259  // address being set is not-NULL because an attempt to set the NULL
260  // HW would result in exception. If these values are not set, the
261  // the default HW addresses (zeroed) should be generated by the
262  // packet filtering class when creating Ethernet header for
263  // outgoing packet.
264  HWAddrPtr src_hw_addr = query_->getLocalHWAddr();
265  if (src_hw_addr) {
266  resp_->setLocalHWAddr(src_hw_addr);
267  }
268  HWAddrPtr dst_hw_addr = query_->getRemoteHWAddr();
269  if (dst_hw_addr) {
270  resp_->setRemoteHWAddr(dst_hw_addr);
271  }
272 
273  // Copy flags from the request to the response per RFC 2131
274  resp_->setFlags(query_->getFlags());
275 }
276 
277 void
278 Dhcpv4Exchange::copyDefaultOptions() {
279  // Let's copy client-id to response. See RFC6842.
280  // It is possible to disable RFC6842 to keep backward compatibility
281  bool echo = CfgMgr::instance().getCurrentCfg()->getEchoClientId();
282  OptionPtr client_id = query_->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
283  if (client_id && echo) {
284  resp_->addOption(client_id);
285  }
286 
287  // If this packet is relayed, we want to copy Relay Agent Info option
288  OptionPtr rai = query_->getOption(DHO_DHCP_AGENT_OPTIONS);
289  if (rai) {
290  resp_->addOption(rai);
291  }
292 
293  // RFC 3011 states about the Subnet Selection Option
294 
295  // "Servers configured to support this option MUST return an
296  // identical copy of the option to any client that sends it,
297  // regardless of whether or not the client requests the option in
298  // a parameter request list. Clients using this option MUST
299  // discard DHCPOFFER or DHCPACK packets that do not contain this
300  // option."
301  OptionPtr subnet_sel = query_->getOption(DHO_SUBNET_SELECTION);
302  if (subnet_sel) {
303  resp_->addOption(subnet_sel);
304  }
305 }
306 
307 void
308 Dhcpv4Exchange::setHostIdentifiers() {
309  const ConstCfgHostOperationsPtr cfg =
310  CfgMgr::instance().getCurrentCfg()->getCfgHostOperations4();
311 
312  // Collect host identifiers. The identifiers are stored in order of preference.
313  // The server will use them in that order to search for host reservations.
314  BOOST_FOREACH(const Host::IdentifierType& id_type,
315  cfg->getIdentifierTypes()) {
316  switch (id_type) {
317  case Host::IDENT_HWADDR:
318  if (context_->hwaddr_ && !context_->hwaddr_->hwaddr_.empty()) {
319  context_->addHostIdentifier(id_type, context_->hwaddr_->hwaddr_);
320  }
321  break;
322 
323  case Host::IDENT_DUID:
324  if (context_->clientid_) {
325  const std::vector<uint8_t>& vec = context_->clientid_->getDuid();
326  if (!vec.empty()) {
327  // Client identifier type = DUID? Client identifier holding a DUID
328  // comprises Type (1 byte), IAID (4 bytes), followed by the actual
329  // DUID. Thus, the minimal length is 6.
330  if ((vec[0] == CLIENT_ID_OPTION_TYPE_DUID) && (vec.size() > 5)) {
331  // Extract DUID, skip IAID.
332  context_->addHostIdentifier(id_type,
333  std::vector<uint8_t>(vec.begin() + 5,
334  vec.end()));
335  }
336  }
337  }
338  break;
339 
341  {
342  OptionPtr rai = query_->getOption(DHO_DHCP_AGENT_OPTIONS);
343  if (rai) {
344  OptionPtr circuit_id_opt = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
345  if (circuit_id_opt) {
346  const OptionBuffer& circuit_id_vec = circuit_id_opt->getData();
347  if (!circuit_id_vec.empty()) {
348  context_->addHostIdentifier(id_type, circuit_id_vec);
349  }
350  }
351  }
352  }
353  break;
354 
356  if (context_->clientid_) {
357  const std::vector<uint8_t>& vec = context_->clientid_->getDuid();
358  if (!vec.empty()) {
359  context_->addHostIdentifier(id_type, vec);
360  }
361  }
362  break;
363  case Host::IDENT_FLEX:
364  {
365  if (!HooksManager::calloutsPresent(Hooks.hook_index_host4_identifier_)) {
366  break;
367  }
368 
369  CalloutHandlePtr callout_handle = getCalloutHandle(context_->query_);
370 
372  std::vector<uint8_t> id;
373 
374  // Use the RAII wrapper to make sure that the callout handle state is
375  // reset when this object goes out of scope. All hook points must do
376  // it to prevent possible circular dependency between the callout
377  // handle and its arguments.
378  ScopedCalloutHandleState callout_handle_state(callout_handle);
379 
380  // Pass incoming packet as argument
381  callout_handle->setArgument("query4", context_->query_);
382  callout_handle->setArgument("id_type", type);
383  callout_handle->setArgument("id_value", id);
384 
385  // Call callouts
386  HooksManager::callCallouts(Hooks.hook_index_host4_identifier_,
387  *callout_handle);
388 
389  callout_handle->getArgument("id_type", type);
390  callout_handle->getArgument("id_value", id);
391 
392  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_CONTINUE) &&
393  !id.empty()) {
394 
396  .arg(Host::getIdentifierAsText(type, &id[0], id.size()));
397 
398  context_->addHostIdentifier(type, id);
399  }
400  break;
401  }
402  default:
403  ;
404  }
405  }
406 }
407 
408 void
410  if (context_->currentHost() && query_) {
411  const ClientClasses& classes = context_->currentHost()->getClientClasses4();
412  for (ClientClasses::const_iterator cclass = classes.cbegin();
413  cclass != classes.cend(); ++cclass) {
414  query_->addClass(*cclass);
415  }
416  }
417 }
418 
419 void
421  ConstHostPtr host = context_->currentHost();
422  // Nothing to do if host reservations not specified for this client.
423  if (host) {
424  if (!host->getNextServer().isV4Zero()) {
425  resp_->setSiaddr(host->getNextServer());
426  }
427 
428  std::string sname = host->getServerHostname();
429  if (!sname.empty()) {
430  resp_->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
431  sname.size());
432  }
433 
434  std::string bootfile = host->getBootFileName();
435  if (!bootfile.empty()) {
436  resp_->setFile(reinterpret_cast<const uint8_t*>(bootfile.c_str()),
437  bootfile.size());
438  }
439  }
440 }
441 
442 const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
443 
444 Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast,
445  const bool direct_response_desired)
446  : io_service_(new IOService()), shutdown_(true), alloc_engine_(), port_(port),
447  use_bcast_(use_bcast), network_state_(new NetworkState(NetworkState::DHCPv4)) {
448 
449  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
450  try {
451  // Port 0 is used for testing purposes where we don't open broadcast
452  // capable sockets. So, set the packet filter handling direct traffic
453  // only if we are in non-test mode.
454  if (port) {
455  // First call to instance() will create IfaceMgr (it's a singleton)
456  // it may throw something if things go wrong.
457  // The 'true' value of the call to setMatchingPacketFilter imposes
458  // that IfaceMgr will try to use the mechanism to respond directly
459  // to the client which doesn't have address assigned. This capability
460  // may be lacking on some OSes, so there is no guarantee that server
461  // will be able to respond directly.
462  IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
463  }
464 
465  // Instantiate allocation engine. The number of allocation attempts equal
466  // to zero indicates that the allocation engine will use the number of
467  // attempts depending on the pool size.
469  false /* false = IPv4 */));
470 
472 
473  } catch (const std::exception &e) {
474  LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what());
475  shutdown_ = true;
476  return;
477  }
478 
479  shutdown_ = false;
480 }
481 
483  // Discard any cached packets or parked packets
484  discardPackets();
485 
486  try {
487  stopD2();
488  } catch(const std::exception& ex) {
489  // Highly unlikely, but lets Report it but go on
490  LOG_ERROR(dhcp4_logger, DHCP4_SRV_D2STOP_ERROR).arg(ex.what());
491  }
492 
493  try {
495  } catch(const std::exception& ex) {
496  // Highly unlikely, but lets Report it but go on
497  LOG_ERROR(dhcp4_logger, DHCP4_SRV_DHCP4O6_ERROR).arg(ex.what());
498  }
499 
501 
502  // The lease manager was instantiated during DHCPv4Srv configuration,
503  // so we should clean up after ourselves.
505 
506  // Explicitly unload hooks
507  HooksManager::getHooksManager().unloadLibraries();
508 }
509 
510 void
512  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_SHUTDOWN_REQUEST);
513  shutdown_ = true;
514 }
515 
517 Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query, bool& drop,
518  bool sanity_only) const {
519 
520  // DHCPv4-over-DHCPv6 is a special (and complex) case
521  if (query->isDhcp4o6()) {
522  return (selectSubnet4o6(query, drop, sanity_only));
523  }
524 
525  Subnet4Ptr subnet;
526 
527  const SubnetSelector& selector = CfgSubnets4::initSelector(query);
528 
529  CfgMgr& cfgmgr = CfgMgr::instance();
530  subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet(selector);
531 
532  // Let's execute all callouts registered for subnet4_select
533  // (skip callouts if the selectSubnet was called to do sanity checks only)
534  if (!sanity_only &&
535  HooksManager::calloutsPresent(Hooks.hook_index_subnet4_select_)) {
536  CalloutHandlePtr callout_handle = getCalloutHandle(query);
537 
538  // Use the RAII wrapper to make sure that the callout handle state is
539  // reset when this object goes out of scope. All hook points must do
540  // it to prevent possible circular dependency between the callout
541  // handle and its arguments.
542  ScopedCalloutHandleState callout_handle_state(callout_handle);
543 
544  // Enable copying options from the packet within hook library.
545  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
546 
547  // Set new arguments
548  callout_handle->setArgument("query4", query);
549  callout_handle->setArgument("subnet4", subnet);
550  callout_handle->setArgument("subnet4collection",
551  cfgmgr.getCurrentCfg()->
552  getCfgSubnets4()->getAll());
553 
554  // Call user (and server-side) callouts
555  HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
556  *callout_handle);
557 
558  // Callouts decided to skip this step. This means that no subnet
559  // will be selected. Packet processing will continue, but it will
560  // be severely limited (i.e. only global options will be assigned)
561  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
563  DHCP4_HOOK_SUBNET4_SELECT_SKIP)
564  .arg(query->getLabel());
565  return (Subnet4Ptr());
566  }
567 
568  // Callouts decided to drop the packet. It is a superset of the
569  // skip case so no subnet will be selected.
570  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
572  DHCP4_HOOK_SUBNET4_SELECT_DROP)
573  .arg(query->getLabel());
574  drop = true;
575  return (Subnet4Ptr());
576  }
577 
578  // Use whatever subnet was specified by the callout
579  callout_handle->getArgument("subnet4", subnet);
580  }
581 
582  if (subnet) {
583  // Log at higher debug level that subnet has been found.
584  LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_SUBNET_SELECTED)
585  .arg(query->getLabel())
586  .arg(subnet->getID());
587  // Log detailed information about the selected subnet at the
588  // lower debug level.
589  LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_DATA)
590  .arg(query->getLabel())
591  .arg(subnet->toText());
592 
593  } else {
595  DHCP4_SUBNET_SELECTION_FAILED)
596  .arg(query->getLabel());
597  }
598 
599  return (subnet);
600 }
601 
603 Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query, bool& drop,
604  bool sanity_only) const {
605 
606  Subnet4Ptr subnet;
607 
608  SubnetSelector selector;
609  selector.ciaddr_ = query->getCiaddr();
610  selector.giaddr_ = query->getGiaddr();
611  selector.local_address_ = query->getLocalAddr();
612  selector.client_classes_ = query->classes_;
613  selector.iface_name_ = query->getIface();
614  // Mark it as DHCPv4-over-DHCPv6
615  selector.dhcp4o6_ = true;
616  // Now the DHCPv6 part
617  selector.remote_address_ = query->getRemoteAddr();
618  selector.first_relay_linkaddr_ = IOAddress("::");
619 
620  // Handle a DHCPv6 relayed query
621  Pkt4o6Ptr query4o6 = boost::dynamic_pointer_cast<Pkt4o6>(query);
622  if (!query4o6) {
623  isc_throw(Unexpected, "Can't get DHCP4o6 message");
624  }
625  const Pkt6Ptr& query6 = query4o6->getPkt6();
626 
627  // Initialize fields specific to relayed messages.
628  if (query6 && !query6->relay_info_.empty()) {
629  BOOST_REVERSE_FOREACH(Pkt6::RelayInfo relay, query6->relay_info_) {
630  if (!relay.linkaddr_.isV6Zero() &&
631  !relay.linkaddr_.isV6LinkLocal()) {
632  selector.first_relay_linkaddr_ = relay.linkaddr_;
633  break;
634  }
635  }
636  selector.interface_id_ =
637  query6->getAnyRelayOption(D6O_INTERFACE_ID,
639  }
640 
641  // If the Subnet Selection option is present, extract its value.
642  OptionPtr sbnsel = query->getOption(DHO_SUBNET_SELECTION);
643  if (sbnsel) {
644  OptionCustomPtr oc = boost::dynamic_pointer_cast<OptionCustom>(sbnsel);
645  if (oc) {
646  selector.option_select_ = oc->readAddress();
647  }
648  }
649 
650  CfgMgr& cfgmgr = CfgMgr::instance();
651  subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet4o6(selector);
652 
653  // Let's execute all callouts registered for subnet4_select.
654  // (skip callouts if the selectSubnet was called to do sanity checks only)
655  if (!sanity_only &&
656  HooksManager::calloutsPresent(Hooks.hook_index_subnet4_select_)) {
657  CalloutHandlePtr callout_handle = getCalloutHandle(query);
658 
659  // Use the RAII wrapper to make sure that the callout handle state is
660  // reset when this object goes out of scope. All hook points must do
661  // it to prevent possible circular dependency between the callout
662  // handle and its arguments.
663  ScopedCalloutHandleState callout_handle_state(callout_handle);
664 
665  // Set new arguments
666  callout_handle->setArgument("query4", query);
667  callout_handle->setArgument("subnet4", subnet);
668  callout_handle->setArgument("subnet4collection",
669  cfgmgr.getCurrentCfg()->
670  getCfgSubnets4()->getAll());
671 
672  // Call user (and server-side) callouts
673  HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
674  *callout_handle);
675 
676  // Callouts decided to skip this step. This means that no subnet
677  // will be selected. Packet processing will continue, but it will
678  // be severely limited (i.e. only global options will be assigned)
679  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
681  DHCP4_HOOK_SUBNET4_SELECT_SKIP)
682  .arg(query->getLabel());
683  return (Subnet4Ptr());
684  }
685 
686  // Callouts decided to drop the packet. It is a superset of the
687  // skip case so no subnet will be selected.
688  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
690  DHCP4_HOOK_SUBNET4_SELECT_DROP)
691  .arg(query->getLabel());
692  drop = true;
693  return (Subnet4Ptr());
694  }
695 
696  // Use whatever subnet was specified by the callout
697  callout_handle->getArgument("subnet4", subnet);
698  }
699 
700  if (subnet) {
701  // Log at higher debug level that subnet has been found.
702  LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_SUBNET_SELECTED)
703  .arg(query->getLabel())
704  .arg(subnet->getID());
705  // Log detailed information about the selected subnet at the
706  // lower debug level.
707  LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_DATA)
708  .arg(query->getLabel())
709  .arg(subnet->toText());
710 
711  } else {
713  DHCP4_SUBNET_SELECTION_FAILED)
714  .arg(query->getLabel());
715  }
716 
717  return (subnet);
718 }
719 
720 Pkt4Ptr
722  return (IfaceMgr::instance().receive4(timeout));
723 }
724 
725 void
727  IfaceMgr::instance().send(packet);
728 }
729 
730 bool
732  while (!shutdown_) {
733  try {
734  run_one();
735  getIOService()->poll();
736  } catch (const std::exception& e) {
737  // General catch-all exception that are not caught by more specific
738  // catches. This one is for exceptions derived from std::exception.
739  LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_STD_EXCEPTION)
740  .arg(e.what());
741  } catch (...) {
742  // General catch-all exception that are not caught by more specific
743  // catches. This one is for other exceptions, not derived from
744  // std::exception.
745  LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_EXCEPTION);
746  }
747  }
748 
749  return (true);
750 }
751 
752 void
754  // client's message and server's response
755  Pkt4Ptr query;
756  Pkt4Ptr rsp;
757 
758  try {
759  // Set select() timeout to 1s. This value should not be modified
760  // because it is important that the select() returns control
761  // frequently so as the IOService can be polled for ready handlers.
762  uint32_t timeout = 1;
763  query = receivePacket(timeout);
764 
765  // Log if packet has arrived. We can't log the detailed information
766  // about the DHCP message because it hasn't been unpacked/parsed
767  // yet, and it can't be parsed at this point because hooks will
768  // have to process it first. The only information available at this
769  // point are: the interface, source address and destination addresses
770  // and ports.
771  if (query) {
772  LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_BUFFER_RECEIVED)
773  .arg(query->getRemoteAddr().toText())
774  .arg(query->getRemotePort())
775  .arg(query->getLocalAddr().toText())
776  .arg(query->getLocalPort())
777  .arg(query->getIface());
778 
779  }
780  // We used to log that the wait was interrupted, but this is no longer
781  // the case. Our wait time is 1s now, so the lack of query packet more
782  // likely means that nothing new appeared within a second, rather than
783  // we were interrupted. And we don't want to print a message every
784  // second.
785 
786  } catch (const SignalInterruptOnSelect&) {
787  // Packet reception interrupted because a signal has been received.
788  // This is not an error because we might have received a SIGTERM,
789  // SIGINT, SIGHUP or SIGCHLD which are handled by the server. For
790  // signals that are not handled by the server we rely on the default
791  // behavior of the system.
792  LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT_SIGNAL)
793  .arg(signal_set_->getNext());
794  } catch (const std::exception& e) {
795  // Log all other errors.
796  LOG_ERROR(packet4_logger, DHCP4_BUFFER_RECEIVE_FAIL).arg(e.what());
797  }
798 
799  // Handle next signal received by the process. It must be called after
800  // an attempt to receive a packet to properly handle server shut down.
801  // The SIGTERM or SIGINT will be received prior to, or during execution
802  // of select() (select is invoked by receivePacket()). When that
803  // happens, select will be interrupted. The signal handler will be
804  // invoked immediately after select(). The handler will set the
805  // shutdown flag and cause the process to terminate before the next
806  // select() function is called. If the function was called before
807  // receivePacket the process could wait up to the duration of timeout
808  // of select() to terminate.
809  try {
810  handleSignal();
811  } catch (const std::exception& e) {
812  // Standard exception occurred. Let's be on the safe side to
813  // catch std::exception.
814  LOG_ERROR(dhcp4_logger, DHCP4_HANDLE_SIGNAL_EXCEPTION)
815  .arg(e.what());
816  }
817 
818  // Timeout may be reached or signal received, which breaks select()
819  // with no reception occurred. No need to log anything here because
820  // we have logged right after the call to receivePacket().
821  if (!query) {
822  return;
823  }
824 
825  // If the DHCP service has been globally disabled, drop the packet.
826  if (!network_state_->isServiceEnabled()) {
828  DHCP4_PACKET_DROP_0008)
829  .arg(query->getLabel());
830  return;
831  } else {
832  processPacket(query, rsp);
833  }
834 
835  if (!rsp) {
836  return;
837  }
838 
839  CalloutHandlePtr callout_handle = getCalloutHandle(query);
840  processPacketBufferSend(callout_handle, rsp);
841 }
842 
843 void
844 Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp, bool allow_packet_park) {
845  // Log reception of the packet. We need to increase it early, as any
846  // failures in unpacking will cause the packet to be dropped. We
847  // will increase type specific statistic further down the road.
848  // See processStatsReceived().
849  isc::stats::StatsMgr::instance().addValue("pkt4-received",
850  static_cast<int64_t>(1));
851 
852  bool skip_unpack = false;
853 
854  // The packet has just been received so contains the uninterpreted wire
855  // data; execute callouts registered for buffer4_receive.
856  if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
857  CalloutHandlePtr callout_handle = getCalloutHandle(query);
858 
859  // Use the RAII wrapper to make sure that the callout handle state is
860  // reset when this object goes out of scope. All hook points must do
861  // it to prevent possible circular dependency between the callout
862  // handle and its arguments.
863  ScopedCalloutHandleState callout_handle_state(callout_handle);
864 
865  // Enable copying options from the packet within hook library.
866  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
867 
868  // Pass incoming packet as argument
869  callout_handle->setArgument("query4", query);
870 
871  // Call callouts
872  HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
873  *callout_handle);
874 
875  // Callouts decided to drop the received packet.
876  // The response (rsp) is null so the caller (run_one) will
877  // immediately return too.
878  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
880  DHCP4_HOOK_BUFFER_RCVD_DROP)
881  .arg(query->getRemoteAddr().toText())
882  .arg(query->getLocalAddr().toText())
883  .arg(query->getIface());
884  return;
885  }
886 
887  // Callouts decided to skip the next processing step. The next
888  // processing step would to parse the packet, so skip at this
889  // stage means that callouts did the parsing already, so server
890  // should skip parsing.
891  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
893  DHCP4_HOOK_BUFFER_RCVD_SKIP)
894  .arg(query->getRemoteAddr().toText())
895  .arg(query->getLocalAddr().toText())
896  .arg(query->getIface());
897  skip_unpack = true;
898  }
899 
900  callout_handle->getArgument("query4", query);
901  }
902 
903  // Unpack the packet information unless the buffer4_receive callouts
904  // indicated they did it
905  if (!skip_unpack) {
906  try {
907  LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_UNPACK)
908  .arg(query->getRemoteAddr().toText())
909  .arg(query->getLocalAddr().toText())
910  .arg(query->getIface());
911  query->unpack();
912  } catch (const SkipRemainingOptionsError& e) {
913  // An option failed to unpack but we are to attempt to process it
914  // anyway. Log it and let's hope for the best.
916  DHCP4_PACKET_OPTIONS_SKIPPED)
917  .arg(e.what());
918  } catch (const std::exception& e) {
919  // Failed to parse the packet.
921  DHCP4_PACKET_DROP_0001)
922  .arg(query->getRemoteAddr().toText())
923  .arg(query->getLocalAddr().toText())
924  .arg(query->getIface())
925  .arg(e.what());
926 
927  // Increase the statistics of parse failures and dropped packets.
928  isc::stats::StatsMgr::instance().addValue("pkt4-parse-failed",
929  static_cast<int64_t>(1));
930  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
931  static_cast<int64_t>(1));
932  return;
933  }
934  }
935 
936  // Update statistics accordingly for received packet.
937  processStatsReceived(query);
938 
939  // Assign this packet to one or more classes if needed. We need to do
940  // this before calling accept(), because getSubnet4() may need client
941  // class information.
942  classifyPacket(query);
943 
944  // Now it is classified the deferred unpacking can be done.
945  deferredUnpack(query);
946 
947  // Check whether the message should be further processed or discarded.
948  // There is no need to log anything here. This function logs by itself.
949  if (!accept(query)) {
950  // Increase the statistic of dropped packets.
951  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
952  static_cast<int64_t>(1));
953  return;
954  }
955 
956  // We have sanity checked (in accept() that the Message Type option
957  // exists, so we can safely get it here.
958  int type = query->getType();
959  LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_PACKET_RECEIVED)
960  .arg(query->getLabel())
961  .arg(query->getName())
962  .arg(type)
963  .arg(query->getRemoteAddr())
964  .arg(query->getLocalAddr())
965  .arg(query->getIface());
967  .arg(query->getLabel())
968  .arg(query->toText());
969 
970  // Let's execute all callouts registered for pkt4_receive
971  if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_receive_)) {
972  CalloutHandlePtr callout_handle = getCalloutHandle(query);
973 
974  // Use the RAII wrapper to make sure that the callout handle state is
975  // reset when this object goes out of scope. All hook points must do
976  // it to prevent possible circular dependency between the callout
977  // handle and its arguments.
978  ScopedCalloutHandleState callout_handle_state(callout_handle);
979 
980  // Enable copying options from the packet within hook library.
981  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
982 
983  // Pass incoming packet as argument
984  callout_handle->setArgument("query4", query);
985 
986  // Call callouts
987  HooksManager::callCallouts(Hooks.hook_index_pkt4_receive_,
988  *callout_handle);
989 
990  // Callouts decided to skip the next processing step. The next
991  // processing step would to process the packet, so skip at this
992  // stage means drop.
993  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
994  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
996  DHCP4_HOOK_PACKET_RCVD_SKIP)
997  .arg(query->getLabel());
998  return;
999  }
1000 
1001  callout_handle->getArgument("query4", query);
1002  }
1003 
1005 
1006  try {
1007  switch (query->getType()) {
1008  case DHCPDISCOVER:
1009  rsp = processDiscover(query);
1010  break;
1011 
1012  case DHCPREQUEST:
1013  // Note that REQUEST is used for many things in DHCPv4: for
1014  // requesting new leases, renewing existing ones and even
1015  // for rebinding.
1016  rsp = processRequest(query, ctx);
1017  break;
1018 
1019  case DHCPRELEASE:
1020  processRelease(query, ctx);
1021  break;
1022 
1023  case DHCPDECLINE:
1024  processDecline(query, ctx);
1025  break;
1026 
1027  case DHCPINFORM:
1028  rsp = processInform(query);
1029  break;
1030 
1031  default:
1032  // Only action is to output a message if debug is enabled,
1033  // and that is covered by the debug statement before the
1034  // "switch" statement.
1035  ;
1036  }
1037  } catch (const std::exception& e) {
1038 
1039  // Catch-all exception (we used to call only isc::Exception, but
1040  // std::exception could potentially be raised and if we don't catch
1041  // it here, it would be caught in main() and the process would
1042  // terminate). Just log the problem and ignore the packet.
1043  // (The problem is logged as a debug message because debug is
1044  // disabled by default - it prevents a DDOS attack based on the
1045  // sending of problem packets.)
1047  DHCP4_PACKET_DROP_0007)
1048  .arg(query->getLabel())
1049  .arg(e.what());
1050 
1051  // Increase the statistic of dropped packets.
1052  isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
1053  static_cast<int64_t>(1));
1054  }
1055 
1056  bool packet_park = false;
1057 
1058  if (ctx && HooksManager::calloutsPresent(Hooks.hook_index_leases4_committed_)) {
1059  CalloutHandlePtr callout_handle = getCalloutHandle(query);
1060 
1061  // Use the RAII wrapper to make sure that the callout handle state is
1062  // reset when this object goes out of scope. All hook points must do
1063  // it to prevent possible circular dependency between the callout
1064  // handle and its arguments.
1065  ScopedCalloutHandleState callout_handle_state(callout_handle);
1066 
1067  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
1068 
1069  // Also pass the corresponding query packet as argument
1070  callout_handle->setArgument("query4", query);
1071 
1072  Lease4CollectionPtr new_leases(new Lease4Collection());
1073  if (ctx->new_lease_) {
1074  new_leases->push_back(ctx->new_lease_);
1075  }
1076  callout_handle->setArgument("leases4", new_leases);
1077 
1078  Lease4CollectionPtr deleted_leases(new Lease4Collection());
1079  if (ctx->old_lease_) {
1080  if ((!ctx->new_lease_) || (ctx->new_lease_->addr_ != ctx->old_lease_->addr_)) {
1081  deleted_leases->push_back(ctx->old_lease_);
1082  }
1083  }
1084  callout_handle->setArgument("deleted_leases4", deleted_leases);
1085 
1086  // Call all installed callouts
1087  HooksManager::callCallouts(Hooks.hook_index_leases4_committed_,
1088  *callout_handle);
1089 
1090  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1092  DHCP4_HOOK_LEASES4_COMMITTED_DROP)
1093  .arg(query->getLabel());
1094  rsp.reset();
1095 
1096  } else if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK)
1097  && allow_packet_park) {
1098  packet_park = true;
1099  }
1100  }
1101 
1102  if (!rsp) {
1103  return;
1104  }
1105 
1106  // PARKING SPOT after leases4_committed hook point.
1107  CalloutHandlePtr callout_handle = getCalloutHandle(query);
1108  if (packet_park) {
1110  DHCP4_HOOK_LEASES4_COMMITTED_PARK)
1111  .arg(query->getLabel());
1112 
1113  // Park the packet. The function we bind here will be executed when the hook
1114  // library unparks the packet.
1115  HooksManager::park("leases4_committed", query,
1116  [this, callout_handle, query, rsp]() mutable {
1117  processPacketPktSend(callout_handle, query, rsp);
1118  processPacketBufferSend(callout_handle, rsp);
1119  });
1120 
1121  // If we have parked the packet, let's reset the pointer to the
1122  // response to indicate to the caller that it should return, as
1123  // the packet processing will continue via the callback.
1124  rsp.reset();
1125 
1126  } else {
1127  processPacketPktSend(callout_handle, query, rsp);
1128  }
1129 }
1130 
1131 void
1133  Pkt4Ptr& query, Pkt4Ptr& rsp) {
1134  if (!rsp) {
1135  return;
1136  }
1137 
1138  // Specifies if server should do the packing
1139  bool skip_pack = false;
1140 
1141  // Execute all callouts registered for pkt4_send
1142  if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_send_)) {
1143 
1144  // Use the RAII wrapper to make sure that the callout handle state is
1145  // reset when this object goes out of scope. All hook points must do
1146  // it to prevent possible circular dependency between the callout
1147  // handle and its arguments.
1148  ScopedCalloutHandleState callout_handle_state(callout_handle);
1149 
1150  // Enable copying options from the query and response packets within
1151  // hook library.
1152  ScopedEnableOptionsCopy<Pkt4> query_resp_options_copy(query, rsp);
1153 
1154  // Set our response
1155  callout_handle->setArgument("response4", rsp);
1156 
1157  // Also pass the corresponding query packet as argument
1158  callout_handle->setArgument("query4", query);
1159 
1160  // Call all installed callouts
1161  HooksManager::callCallouts(Hooks.hook_index_pkt4_send_,
1162  *callout_handle);
1163 
1164  // Callouts decided to skip the next processing step. The next
1165  // processing step would to send the packet, so skip at this
1166  // stage means "drop response".
1167  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1168  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1170  DHCP4_HOOK_PACKET_SEND_SKIP)
1171  .arg(query->getLabel());
1172  skip_pack = true;
1173  }
1174  }
1175 
1176  if (!skip_pack) {
1177  try {
1178  LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_PACK)
1179  .arg(rsp->getLabel());
1180  rsp->pack();
1181  } catch (const std::exception& e) {
1182  LOG_ERROR(options4_logger, DHCP4_PACKET_PACK_FAIL)
1183  .arg(rsp->getLabel())
1184  .arg(e.what());
1185  }
1186  }
1187 }
1188 
1189 void
1191  Pkt4Ptr& rsp) {
1192  if (!rsp) {
1193  return;
1194  }
1195 
1196  try {
1197  // Now all fields and options are constructed into output wire buffer.
1198  // Option objects modification does not make sense anymore. Hooks
1199  // can only manipulate wire buffer at this stage.
1200  // Let's execute all callouts registered for buffer4_send
1201  if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
1202 
1203  // Use the RAII wrapper to make sure that the callout handle state is
1204  // reset when this object goes out of scope. All hook points must do
1205  // it to prevent possible circular dependency between the callout
1206  // handle and its arguments.
1207  ScopedCalloutHandleState callout_handle_state(callout_handle);
1208 
1209  // Enable copying options from the packet within hook library.
1210  ScopedEnableOptionsCopy<Pkt4> resp4_options_copy(rsp);
1211 
1212  // Pass incoming packet as argument
1213  callout_handle->setArgument("response4", rsp);
1214 
1215  // Call callouts
1216  HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
1217  *callout_handle);
1218 
1219  // Callouts decided to skip the next processing step. The next
1220  // processing step would to parse the packet, so skip at this
1221  // stage means drop.
1222  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1223  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1225  DHCP4_HOOK_BUFFER_SEND_SKIP)
1226  .arg(rsp->getLabel());
1227  return;
1228  }
1229 
1230  callout_handle->getArgument("response4", rsp);
1231  }
1232 
1233  LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_PACKET_SEND)
1234  .arg(rsp->getLabel())
1235  .arg(rsp->getName())
1236  .arg(static_cast<int>(rsp->getType()))
1237  .arg(rsp->getLocalAddr().isV4Zero() ? "*" : rsp->getLocalAddr().toText())
1238  .arg(rsp->getLocalPort())
1239  .arg(rsp->getRemoteAddr())
1240  .arg(rsp->getRemotePort())
1241  .arg(rsp->getIface().empty() ? "to be determined from routing" :
1242  rsp->getIface());
1243 
1245  DHCP4_RESPONSE_DATA)
1246  .arg(rsp->getLabel())
1247  .arg(rsp->getName())
1248  .arg(static_cast<int>(rsp->getType()))
1249  .arg(rsp->toText());
1250  sendPacket(rsp);
1251 
1252  // Update statistics accordingly for sent packet.
1253  processStatsSent(rsp);
1254 
1255  } catch (const std::exception& e) {
1256  LOG_ERROR(packet4_logger, DHCP4_PACKET_SEND_FAIL)
1257  .arg(rsp->getLabel())
1258  .arg(e.what());
1259  }
1260 }
1261 
1262 string
1264  if (!srvid) {
1265  isc_throw(BadValue, "NULL pointer passed to srvidToString()");
1266  }
1267  boost::shared_ptr<Option4AddrLst> generated =
1268  boost::dynamic_pointer_cast<Option4AddrLst>(srvid);
1269  if (!srvid) {
1270  isc_throw(BadValue, "Pointer to invalid option passed to srvidToString()");
1271  }
1272 
1273  Option4AddrLst::AddressContainer addrs = generated->getAddresses();
1274  if (addrs.size() != 1) {
1275  isc_throw(BadValue, "Malformed option passed to srvidToString(). "
1276  << "Expected to contain a single IPv4 address.");
1277  }
1278 
1279  return (addrs[0].toText());
1280 }
1281 
1282 void
1284 
1285  // Do not append generated server identifier if there is one appended already.
1286  // This is when explicitly configured server identifier option is present.
1287  if (ex.getResponse()->getOption(DHO_DHCP_SERVER_IDENTIFIER)) {
1288  return;
1289  }
1290 
1291  // Use local address on which the packet has been received as a
1292  // server identifier. In some cases it may be a different address,
1293  // e.g. broadcast packet or DHCPv4o6 packet.
1294  IOAddress local_addr = ex.getQuery()->getLocalAddr();
1295  Pkt4Ptr query = ex.getQuery();
1296 
1297  if (local_addr.isV4Bcast() || query->isDhcp4o6()) {
1298  SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
1299  local_addr = sock_info.addr_;
1300  }
1301 
1303  local_addr));
1304  ex.getResponse()->addOption(opt_srvid);
1305 }
1306 
1307 void
1309  CfgOptionList& co_list = ex.getCfgOptionList();
1310 
1311  // Retrieve subnet.
1312  Subnet4Ptr subnet = ex.getContext()->subnet_;
1313  if (!subnet) {
1314  // All methods using the CfgOptionList object return soon when
1315  // there is no subnet so do the same
1316  return;
1317  }
1318 
1319  // Firstly, host specific options.
1320  const ConstHostPtr& host = ex.getContext()->currentHost();
1321  if (host && !host->getCfgOption4()->empty()) {
1322  co_list.push_back(host->getCfgOption4());
1323  }
1324 
1325  // Secondly, pool specific options.
1326  Pkt4Ptr resp = ex.getResponse();
1328  if (resp) {
1329  addr = resp->getYiaddr();
1330  }
1331  if (!addr.isV4Zero()) {
1332  PoolPtr pool = subnet->getPool(Lease::TYPE_V4, addr, false);
1333  if (pool && !pool->getCfgOption()->empty()) {
1334  co_list.push_back(pool->getCfgOption());
1335  }
1336  }
1337 
1338  // Thirdly, subnet configured options.
1339  if (!subnet->getCfgOption()->empty()) {
1340  co_list.push_back(subnet->getCfgOption());
1341  }
1342 
1343  // Forthly, shared network specific options.
1344  SharedNetwork4Ptr network;
1345  subnet->getSharedNetwork(network);
1346  if (network && !network->getCfgOption()->empty()) {
1347  co_list.push_back(network->getCfgOption());
1348  }
1349 
1350  // Each class in the incoming packet
1351  const ClientClasses& classes = ex.getQuery()->getClasses();
1352  for (ClientClasses::const_iterator cclass = classes.cbegin();
1353  cclass != classes.cend(); ++cclass) {
1354  // Find the client class definition for this class
1355  const ClientClassDefPtr& ccdef = CfgMgr::instance().getCurrentCfg()->
1356  getClientClassDictionary()->findClass(*cclass);
1357  if (!ccdef) {
1358  // Not found: the class is built-in or not configured
1359  if (!isClientClassBuiltIn(*cclass)) {
1360  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNCONFIGURED)
1361  .arg(ex.getQuery()->getLabel())
1362  .arg(*cclass);
1363  }
1364  // Skip it
1365  continue;
1366  }
1367  if (ccdef->getCfgOption()->empty()) {
1368  // Skip classes which don't configure options
1369  continue;
1370  }
1371  co_list.push_back(ccdef->getCfgOption());
1372  }
1373 
1374  // Last global options
1375  if (!CfgMgr::instance().getCurrentCfg()->getCfgOption()->empty()) {
1376  co_list.push_back(CfgMgr::instance().getCurrentCfg()->getCfgOption());
1377  }
1378 }
1379 
1380 void
1382  // Get the subnet relevant for the client. We will need it
1383  // to get the options associated with it.
1384  Subnet4Ptr subnet = ex.getContext()->subnet_;
1385  // If we can't find the subnet for the client there is no way
1386  // to get the options to be sent to a client. We don't log an
1387  // error because it will be logged by the assignLease method
1388  // anyway.
1389  if (!subnet) {
1390  return;
1391  }
1392 
1393  // Unlikely short cut
1394  const CfgOptionList& co_list = ex.getCfgOptionList();
1395  if (co_list.empty()) {
1396  return;
1397  }
1398 
1399  Pkt4Ptr query = ex.getQuery();
1400  Pkt4Ptr resp = ex.getResponse();
1401  std::vector<uint8_t> requested_opts;
1402 
1403  // try to get the 'Parameter Request List' option which holds the
1404  // codes of requested options.
1405  OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast<
1407  // Get the codes of requested options.
1408  if (option_prl) {
1409  requested_opts = option_prl->getValues();
1410  }
1411  // Iterate on the configured option list to add persistent options
1412  for (CfgOptionList::const_iterator copts = co_list.begin();
1413  copts != co_list.end(); ++copts) {
1414  const OptionContainerPtr& opts = (*copts)->getAll(DHCP4_OPTION_SPACE);
1415  if (!opts) {
1416  continue;
1417  }
1418  // Get persistent options
1419  const OptionContainerPersistIndex& idx = opts->get<2>();
1420  const OptionContainerPersistRange& range = idx.equal_range(true);
1421  for (OptionContainerPersistIndex::const_iterator desc = range.first;
1422  desc != range.second; ++desc) {
1423  // Add the persistent option code to requested options
1424  if (desc->option_) {
1425  uint8_t code = static_cast<uint8_t>(desc->option_->getType());
1426  requested_opts.push_back(code);
1427  }
1428  }
1429  }
1430 
1431  // For each requested option code get the instance of the option
1432  // to be returned to the client.
1433  for (std::vector<uint8_t>::const_iterator opt = requested_opts.begin();
1434  opt != requested_opts.end(); ++opt) {
1435  // Add nothing when it is already there
1436  if (!resp->getOption(*opt)) {
1437  // Iterate on the configured option list
1438  for (CfgOptionList::const_iterator copts = co_list.begin();
1439  copts != co_list.end(); ++copts) {
1440  OptionDescriptor desc = (*copts)->get(DHCP4_OPTION_SPACE, *opt);
1441  // Got it: add it and jump to the outer loop
1442  if (desc.option_) {
1443  resp->addOption(desc.option_);
1444  break;
1445  }
1446  }
1447  }
1448  }
1449 }
1450 
1451 void
1453  // Get the configured subnet suitable for the incoming packet.
1454  Subnet4Ptr subnet = ex.getContext()->subnet_;
1455  // Leave if there is no subnet matching the incoming packet.
1456  // There is no need to log the error message here because
1457  // it will be logged in the assignLease() when it fails to
1458  // pick the suitable subnet. We don't want to duplicate
1459  // error messages in such case.
1460  if (!subnet) {
1461  return;
1462  }
1463 
1464  // Unlikely short cut
1465  const CfgOptionList& co_list = ex.getCfgOptionList();
1466  if (co_list.empty()) {
1467  return;
1468  }
1469 
1470  // Try to get the vendor option
1471  boost::shared_ptr<OptionVendor> vendor_req = boost::dynamic_pointer_cast<
1472  OptionVendor>(ex.getQuery()->getOption(DHO_VIVSO_SUBOPTIONS));
1473  if (!vendor_req) {
1474  return;
1475  }
1476 
1477  uint32_t vendor_id = vendor_req->getVendorId();
1478  std::vector<uint8_t> requested_opts;
1479 
1480  // Let's try to get ORO within that vendor-option
1483  OptionUint8ArrayPtr oro =
1484  boost::dynamic_pointer_cast<OptionUint8Array>(vendor_req->getOption(DOCSIS3_V4_ORO));
1485  // Get the list of options that client requested.
1486  if (oro) {
1487  requested_opts = oro->getValues();
1488  }
1489  // Iterate on the configured option list to add persistent options
1490  for (CfgOptionList::const_iterator copts = co_list.begin();
1491  copts != co_list.end(); ++copts) {
1492  const OptionContainerPtr& opts = (*copts)->getAll(vendor_id);
1493  if (!opts) {
1494  continue;
1495  }
1496  // Get persistent options
1497  const OptionContainerPersistIndex& idx = opts->get<2>();
1498  const OptionContainerPersistRange& range = idx.equal_range(true);
1499  for (OptionContainerPersistIndex::const_iterator desc = range.first;
1500  desc != range.second; ++desc) {
1501  // Add the persistent option code to requested options
1502  if (desc->option_) {
1503  uint8_t code = static_cast<uint8_t>(desc->option_->getType());
1504  requested_opts.push_back(code);
1505  }
1506  }
1507  }
1508 
1509  // If there is nothing to add don't do anything then.
1510  if (requested_opts.empty()) {
1511  return;
1512  }
1513 
1514  boost::shared_ptr<OptionVendor> vendor_rsp(new OptionVendor(Option::V4, vendor_id));
1515 
1516  // Get the list of options that client requested.
1517  bool added = false;
1518  for (std::vector<uint8_t>::const_iterator code = requested_opts.begin();
1519  code != requested_opts.end(); ++code) {
1520  if (!vendor_rsp->getOption(*code)) {
1521  for (CfgOptionList::const_iterator copts = co_list.begin();
1522  copts != co_list.end(); ++copts) {
1523  OptionDescriptor desc = (*copts)->get(vendor_id, *code);
1524  if (desc.option_) {
1525  vendor_rsp->addOption(desc.option_);
1526  added = true;
1527  break;
1528  }
1529  }
1530  }
1531 
1532  if (added) {
1533  ex.getResponse()->addOption(vendor_rsp);
1534  }
1535  }
1536 }
1537 
1538 
1539 void
1541  // Identify options that we always want to send to the
1542  // client (if they are configured).
1543  static const uint16_t required_options[] = {
1544  DHO_ROUTERS,
1548 
1549  static size_t required_options_size =
1550  sizeof(required_options) / sizeof(required_options[0]);
1551 
1552  // Get the subnet.
1553  Subnet4Ptr subnet = ex.getContext()->subnet_;
1554  if (!subnet) {
1555  return;
1556  }
1557 
1558  // Unlikely short cut
1559  const CfgOptionList& co_list = ex.getCfgOptionList();
1560  if (co_list.empty()) {
1561  return;
1562  }
1563 
1564  Pkt4Ptr resp = ex.getResponse();
1565 
1566  // Try to find all 'required' options in the outgoing
1567  // message. Those that are not present will be added.
1568  for (int i = 0; i < required_options_size; ++i) {
1569  OptionPtr opt = resp->getOption(required_options[i]);
1570  if (!opt) {
1571  // Check whether option has been configured.
1572  for (CfgOptionList::const_iterator copts = co_list.begin();
1573  copts != co_list.end(); ++copts) {
1574  OptionDescriptor desc = (*copts)->get(DHCP4_OPTION_SPACE,
1575  required_options[i]);
1576  if (desc.option_) {
1577  resp->addOption(desc.option_);
1578  break;
1579  }
1580  }
1581  }
1582  }
1583 }
1584 
1585 void
1587  // It is possible that client has sent both Client FQDN and Hostname
1588  // option. In such case, server should prefer Client FQDN option and
1589  // ignore the Hostname option.
1590  try {
1591  Pkt4Ptr resp = ex.getResponse();
1592  Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn>
1593  (ex.getQuery()->getOption(DHO_FQDN));
1594  if (fqdn) {
1595  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENT_FQDN_PROCESS)
1596  .arg(ex.getQuery()->getLabel());
1597  processClientFqdnOption(ex);
1598 
1599  } else {
1601  DHCP4_CLIENT_HOSTNAME_PROCESS)
1602  .arg(ex.getQuery()->getLabel());
1603  processHostnameOption(ex);
1604  }
1605  } catch (const Exception& e) {
1606  // In some rare cases it is possible that the client's name processing
1607  // fails. For example, the Hostname option may be malformed, or there
1608  // may be an error in the server's logic which would cause multiple
1609  // attempts to add the same option to the response message. This
1610  // error message aggregates all these errors so they can be diagnosed
1611  // from the log. We don't want to throw an exception here because,
1612  // it will impact the processing of the whole packet. We rather want
1613  // the processing to continue, even if the client's name is wrong.
1614  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_NAME_PROC_FAIL)
1615  .arg(ex.getQuery()->getLabel())
1616  .arg(e.what());
1617  }
1618 }
1619 
1620 void
1621 Dhcpv4Srv::processClientFqdnOption(Dhcpv4Exchange& ex) {
1622  // Obtain the FQDN option from the client's message.
1623  Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
1624  Option4ClientFqdn>(ex.getQuery()->getOption(DHO_FQDN));
1625 
1626  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_FQDN_DATA)
1627  .arg(ex.getQuery()->getLabel())
1628  .arg(fqdn->toText());
1629 
1630  // Create the DHCPv4 Client FQDN Option to be included in the server's
1631  // response to a client.
1632  Option4ClientFqdnPtr fqdn_resp(new Option4ClientFqdn(*fqdn));
1633 
1634  // Set the server S, N, and O flags based on client's flags and
1635  // current configuration.
1637  d2_mgr.adjustFqdnFlags<Option4ClientFqdn>(*fqdn, *fqdn_resp);
1638 
1639  // Carry over the client's E flag.
1642 
1643  if (ex.getContext()->currentHost() &&
1644  !ex.getContext()->currentHost()->getHostname().empty()) {
1646  fqdn_resp->setDomainName(d2_mgr.qualifyName(ex.getContext()->currentHost()->getHostname(),
1647  true), Option4ClientFqdn::FULL);
1648 
1649  } else {
1650  // Adjust the domain name based on domain name value and type sent by the
1651  // client and current configuration.
1652  d2_mgr.adjustDomainName<Option4ClientFqdn>(*fqdn, *fqdn_resp);
1653  }
1654 
1655  // Add FQDN option to the response message. Note that, there may be some
1656  // cases when server may choose not to include the FQDN option in a
1657  // response to a client. In such cases, the FQDN should be removed from the
1658  // outgoing message. In theory we could cease to include the FQDN option
1659  // in this function until it is confirmed that it should be included.
1660  // However, we include it here for simplicity. Functions used to acquire
1661  // lease for a client will scan the response message for FQDN and if it
1662  // is found they will take necessary actions to store the FQDN information
1663  // in the lease database as well as to generate NameChangeRequests to DNS.
1664  // If we don't store the option in the response message, we will have to
1665  // propagate it in the different way to the functions which acquire the
1666  // lease. This would require modifications to the API of this class.
1667  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_RESPONSE_FQDN_DATA)
1668  .arg(ex.getQuery()->getLabel())
1669  .arg(fqdn_resp->toText());
1670  ex.getResponse()->addOption(fqdn_resp);
1671 }
1672 
1673 void
1674 Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) {
1675  // Fetch D2 configuration.
1677 
1678  // Obtain the Hostname option from the client's message.
1679  OptionStringPtr opt_hostname = boost::dynamic_pointer_cast<OptionString>
1680  (ex.getQuery()->getOption(DHO_HOST_NAME));
1681 
1682  if (opt_hostname) {
1683  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_HOSTNAME_DATA)
1684  .arg(ex.getQuery()->getLabel())
1685  .arg(opt_hostname->getValue());
1686  }
1687 
1689 
1690  // Hostname reservations take precedence over any other configuration,
1691  // i.e. DDNS configuration.
1692  if (ctx->currentHost() && !ctx->currentHost()->getHostname().empty()) {
1693  // In order to send a reserved hostname value we expect that at least
1694  // one of the following is the case: the client has sent us a hostname
1695  // option, or the client has sent Parameter Request List option with
1696  // the hostname option code included.
1697 
1698  // It is going to be less expensive to first check the presence of the
1699  // hostname option.
1700  bool should_send_hostname = static_cast<bool>(opt_hostname);
1701  // Hostname option is not present, so we have to check PRL option.
1702  if (!should_send_hostname) {
1704  option_prl = boost::dynamic_pointer_cast<OptionUint8Array>
1705  (ex.getQuery()->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
1706  if (option_prl) {
1707  // PRL option exists, so check if the hostname option code is
1708  // included in it.
1709  const std::vector<uint8_t>&
1710  requested_opts = option_prl->getValues();
1711  if (std::find(requested_opts.begin(), requested_opts.end(),
1712  DHO_HOST_NAME) != requested_opts.end()) {
1713  // Client has requested hostname via Parameter Request
1714  // List option.
1715  should_send_hostname = true;
1716  }
1717  }
1718  }
1719 
1720  // If the hostname or PRL option indicates that the server should
1721  // send back a hostname option, send this option with a reserved
1722  // name for this client.
1723  if (should_send_hostname) {
1724  std::string hostname =
1725  d2_mgr.qualifyName(ctx->currentHost()->getHostname(), false);
1726 
1727  // Convert hostname to lower case.
1728  boost::algorithm::to_lower(hostname);
1729 
1731  DHCP4_RESERVED_HOSTNAME_ASSIGNED)
1732  .arg(ex.getQuery()->getLabel())
1733  .arg(hostname);
1734  OptionStringPtr opt_hostname_resp(new OptionString(Option::V4,
1735  DHO_HOST_NAME,
1736  hostname));
1737  ex.getResponse()->addOption(opt_hostname_resp);
1738 
1739  // We're done here.
1740  return;
1741  }
1742  }
1743 
1744  // There is no reservation for this client or the client hasn't requested
1745  // hostname option. There is still a possibility that we'll have to send
1746  // hostname option to this client if the client has included hostname option
1747  // but there is no reservation, or the configuration of the server requires
1748  // that we send the option regardless.
1749 
1750  D2ClientConfig::ReplaceClientNameMode replace_name_mode =
1751  d2_mgr.getD2ClientConfig()->getReplaceClientNameMode();
1752 
1753  // If we don't have a hostname then either we'll supply it or do nothing.
1754  if (!opt_hostname) {
1755  // If we're configured to supply it then add it to the response.
1756  // Use the root domain to signal later on that we should replace it.
1757  if (replace_name_mode == D2ClientConfig::RCM_ALWAYS ||
1758  replace_name_mode == D2ClientConfig::RCM_WHEN_NOT_PRESENT) {
1760  DHCP4_GENERATE_FQDN)
1761  .arg(ex.getQuery()->getLabel());
1762  OptionStringPtr opt_hostname_resp(new OptionString(Option::V4,
1763  DHO_HOST_NAME,
1764  "."));
1765  ex.getResponse()->addOption(opt_hostname_resp);
1766  }
1767 
1768  return;
1769  }
1770 
1771  // Client sent us a hostname option so figure out what to do with it.
1772  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_HOSTNAME_DATA)
1773  .arg(ex.getQuery()->getLabel())
1774  .arg(opt_hostname->getValue());
1775 
1776  std::string hostname = isc::util::str::trim(opt_hostname->getValue());
1777  unsigned int label_count = OptionDataTypeUtil::getLabelCount(hostname);
1778  // The hostname option sent by the client should be at least 1 octet long.
1779  // If it isn't we ignore this option. (Per RFC 2131, section 3.14)
1782  if (label_count == 0) {
1783  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_EMPTY_HOSTNAME)
1784  .arg(ex.getQuery()->getLabel());
1785  return;
1786  }
1787 
1788  // Stores the value we eventually use, so we can send it back.
1789  OptionStringPtr opt_hostname_resp;
1790 
1791  // The hostname option may be unqualified or fully qualified. The lab_count
1792  // holds the number of labels for the name. The number of 1 means that
1793  // there is only root label "." (even for unqualified names, as the
1794  // getLabelCount function treats each name as a fully qualified one).
1795  // By checking the number of labels present in the hostname we may infer
1796  // whether client has sent the fully qualified or unqualified hostname.
1797 
1798  if ((replace_name_mode == D2ClientConfig::RCM_ALWAYS ||
1799  replace_name_mode == D2ClientConfig::RCM_WHEN_PRESENT)
1800  || label_count < 2) {
1801  // Set to root domain to signal later on that we should replace it.
1802  // DHO_HOST_NAME is a string option which cannot be empty.
1810  opt_hostname_resp.reset(new OptionString(Option::V4, DHO_HOST_NAME, "."));
1811  } else {
1812  // Sanitize the name the client sent us, if we're configured to do so.
1814  ->getHostnameSanitizer();
1815  if (sanitizer) {
1816  hostname = sanitizer->scrub(hostname);
1817  }
1818 
1819  // Convert hostname to lower case.
1820  boost::algorithm::to_lower(hostname);
1821 
1822  if (label_count == 2) {
1823  // If there are two labels, it means that the client has specified
1824  // the unqualified name. We have to concatenate the unqualified name
1825  // with the domain name. The false value passed as a second argument
1826  // indicates that the trailing dot should not be appended to the
1827  // hostname. We don't want to append the trailing dot because
1828  // we don't know whether the hostname is partial or not and some
1829  // clients do not handle the hostnames with the trailing dot.
1830  opt_hostname_resp.reset(new OptionString(Option::V4, DHO_HOST_NAME,
1831  d2_mgr.qualifyName(hostname, false)));
1832  } else {
1833  opt_hostname_resp.reset(new OptionString(Option::V4, DHO_HOST_NAME, hostname));
1834  }
1835  }
1836 
1837  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_RESPONSE_HOSTNAME_DATA)
1838  .arg(ex.getQuery()->getLabel())
1839  .arg(opt_hostname_resp->getValue());
1840  ex.getResponse()->addOption(opt_hostname_resp);
1841 }
1842 
1843 void
1845  const Lease4Ptr& old_lease) {
1846  if (!lease) {
1848  "NULL lease specified when creating NameChangeRequest");
1849 
1850  } else if (!old_lease || !lease->hasIdenticalFqdn(*old_lease)) {
1851  if (old_lease) {
1852  // Queue's up a remove of the old lease's DNS (if needed)
1853  queueNCR(CHG_REMOVE, old_lease);
1854  }
1855 
1856  // We may need to generate the NameChangeRequest for the new lease. It
1857  // will be generated only if hostname is set and if forward or reverse
1858  // update has been requested.
1859  queueNCR(CHG_ADD, lease);
1860  }
1861 }
1862 
1863 void
1865  // Get the pointers to the query and the response messages.
1866  Pkt4Ptr query = ex.getQuery();
1867  Pkt4Ptr resp = ex.getResponse();
1868 
1869  // Get the context.
1871 
1872  // Subnet should have been already selected when the context was created.
1873  Subnet4Ptr subnet = ctx->subnet_;
1874  if (!subnet) {
1875  // This particular client is out of luck today. We do not have
1876  // information about the subnet he is connected to. This likely means
1877  // misconfiguration of the server (or some relays).
1878 
1879  // Perhaps this should be logged on some higher level?
1880  LOG_ERROR(bad_packet4_logger, DHCP4_PACKET_NAK_0001)
1881  .arg(query->getLabel())
1882  .arg(query->getRemoteAddr().toText())
1883  .arg(query->getName());
1884  resp->setType(DHCPNAK);
1885  resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
1886  return;
1887  }
1888 
1889 
1890  // Get the server identifier. It will be used to determine the state
1891  // of the client.
1892  OptionCustomPtr opt_serverid = boost::dynamic_pointer_cast<
1893  OptionCustom>(query->getOption(DHO_DHCP_SERVER_IDENTIFIER));
1894 
1895  // Check if the client has sent a requested IP address option or
1896  // ciaddr.
1897  OptionCustomPtr opt_requested_address = boost::dynamic_pointer_cast<
1898  OptionCustom>(query->getOption(DHO_DHCP_REQUESTED_ADDRESS));
1900  if (opt_requested_address) {
1901  hint = opt_requested_address->readAddress();
1902 
1903  } else if (!query->getCiaddr().isV4Zero()) {
1904  hint = query->getCiaddr();
1905 
1906  }
1907 
1908  HWAddrPtr hwaddr = query->getHWAddr();
1909 
1910  // "Fake" allocation is processing of DISCOVER message. We pretend to do an
1911  // allocation, but we do not put the lease in the database. That is ok,
1912  // because we do not guarantee that the user will get that exact lease. If
1913  // the user selects this server to do actual allocation (i.e. sends REQUEST)
1914  // it should include this hint. That will help us during the actual lease
1915  // allocation.
1916  bool fake_allocation = (query->getType() == DHCPDISCOVER);
1917 
1918  // Get client-id. It is not mandatory in DHCPv4.
1919  ClientIdPtr client_id = ex.getContext()->clientid_;
1920 
1921  // If there is no server id and there is a Requested IP Address option
1922  // the client is in the INIT-REBOOT state in which the server has to
1923  // determine whether the client's notion of the address is correct
1924  // and whether the client is known, i.e., has a lease.
1925  if (!fake_allocation && !opt_serverid && opt_requested_address) {
1926 
1927  LOG_INFO(lease4_logger, DHCP4_INIT_REBOOT)
1928  .arg(query->getLabel())
1929  .arg(hint.toText());
1930 
1931  Lease4Ptr lease;
1932  Subnet4Ptr original_subnet = subnet;
1933 
1934  // We used to issue a separate query (two actually: one for client-id
1935  // and another one for hw-addr for) each subnet in the shared network.
1936  // That was horribly inefficient if the client didn't have any lease
1937  // (or there were many subnets and the client happended to be in one
1938  // of the last subnets).
1939  //
1940  // We now issue at most two queries: get all the leases for specific
1941  // client-id and then get all leases for specific hw-address.
1942  if (client_id) {
1943 
1944  // Get all the leases for this client-id
1945  Lease4Collection leases_client_id = LeaseMgrFactory::instance().getLease4(*client_id);
1946  if (!leases_client_id.empty()) {
1947  Subnet4Ptr s = original_subnet;
1948 
1949  // Among those returned try to find a lease that belongs to
1950  // current shared network.
1951  while (s) {
1952  for (auto l = leases_client_id.begin(); l != leases_client_id.end(); ++l) {
1953  if ((*l)->subnet_id_ == s->getID()) {
1954  lease = *l;
1955  break;
1956  }
1957  }
1958 
1959  if (lease) {
1960  break;
1961 
1962  } else {
1963  s = s->getNextSubnet(original_subnet, query->getClasses());
1964  }
1965  }
1966  }
1967  }
1968 
1969  // If we haven't found a lease yet, try again by hardware-address.
1970  // The logic is the same.
1971  if (!lease && hwaddr) {
1972 
1973  // Get all leases for this particular hw-address.
1974  Lease4Collection leases_hwaddr = LeaseMgrFactory::instance().getLease4(*hwaddr);
1975  if (!leases_hwaddr.empty()) {
1976  Subnet4Ptr s = original_subnet;
1977 
1978  // Pick one that belongs to a subnet in this shared network.
1979  while (s) {
1980  for (auto l = leases_hwaddr.begin(); l != leases_hwaddr.end(); ++l) {
1981  if ((*l)->subnet_id_ == s->getID()) {
1982  lease = *l;
1983  break;
1984  }
1985  }
1986 
1987  if (lease) {
1988  break;
1989 
1990  } else {
1991  s = s->getNextSubnet(original_subnet, query->getClasses());
1992  }
1993  }
1994  }
1995  }
1996 
1997  // Check the first error case: unknown client. We check this before
1998  // validating the address sent because we don't want to respond if
1999  // we don't know this client, except if we're authoritative.
2000  bool authoritative = original_subnet->getAuthoritative();
2001  bool known_client = lease && lease->belongsToClient(hwaddr, client_id);
2002  if (!authoritative && !known_client) {
2004  DHCP4_NO_LEASE_INIT_REBOOT)
2005  .arg(query->getLabel())
2006  .arg(hint.toText());
2007 
2008  ex.deleteResponse();
2009  return;
2010  }
2011 
2012  // If we know this client, check if his notion of the IP address is
2013  // correct, if we don't know him, check if we are authoritative.
2014  if ((known_client && (lease->addr_ != hint)) ||
2015  (!known_client && authoritative)) {
2017  DHCP4_PACKET_NAK_0002)
2018  .arg(query->getLabel())
2019  .arg(hint.toText());
2020 
2021  resp->setType(DHCPNAK);
2022  resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2023  return;
2024  }
2025  }
2026 
2027 
2028  CalloutHandlePtr callout_handle = getCalloutHandle(query);
2029 
2030  std::string hostname;
2031  bool fqdn_fwd = false;
2032  bool fqdn_rev = false;
2033  OptionStringPtr opt_hostname;
2034  Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
2035  Option4ClientFqdn>(resp->getOption(DHO_FQDN));
2036  if (fqdn) {
2037  hostname = fqdn->getDomainName();
2039  fqdn_fwd,
2040  fqdn_rev);
2041  } else {
2042  opt_hostname = boost::dynamic_pointer_cast<OptionString>
2043  (resp->getOption(DHO_HOST_NAME));
2044 
2045  if (opt_hostname) {
2046  hostname = opt_hostname->getValue();
2047  // DHO_HOST_NAME is string option which cannot be blank,
2048  // we use "." to know we should replace it with a fully
2049  // generated name. The local string variable needs to be
2050  // blank in logic below.
2051  if (hostname == ".") {
2052  hostname = "";
2053  }
2056  fqdn_fwd = true;
2057  fqdn_rev = true;
2058  }
2059  }
2060 
2061  // We need to set these values in the context as they haven't been set yet.
2062  ctx->requested_address_ = hint;
2063  ctx->fwd_dns_update_ = fqdn_fwd;
2064  ctx->rev_dns_update_ = fqdn_rev;
2065  ctx->hostname_ = hostname;
2066  ctx->fake_allocation_ = fake_allocation;
2067  ctx->callout_handle_ = callout_handle;
2068 
2069  Lease4Ptr lease = alloc_engine_->allocateLease4(*ctx);
2070 
2071  // Subnet may be modified by the allocation engine, if the initial subnet
2072  // belongs to a shared network.
2073  if (subnet->getID() != ctx->subnet_->getID()) {
2074  SharedNetwork4Ptr network;
2075  subnet->getSharedNetwork(network);
2076  if (network) {
2077  LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_SUBNET_DYNAMICALLY_CHANGED)
2078  .arg(query->getLabel())
2079  .arg(subnet->toText())
2080  .arg(ctx->subnet_->toText())
2081  .arg(network->getName());
2082  }
2083  subnet = ctx->subnet_;
2084  }
2085 
2086  if (lease) {
2087  // We have a lease! Let's set it in the packet and send it back to
2088  // the client.
2089  LOG_INFO(lease4_logger, fake_allocation ? DHCP4_LEASE_ADVERT : DHCP4_LEASE_ALLOC)
2090  .arg(query->getLabel())
2091  .arg(lease->addr_.toText());
2092 
2093  // We're logging this here, because this is the place where we know
2094  // which subnet has been actually used for allocation. If the
2095  // client identifier matching is disabled, we want to make sure that
2096  // the user is notified.
2097  if (!ctx->subnet_->getMatchClientId()) {
2098  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENTID_IGNORED_FOR_LEASES)
2099  .arg(ctx->query_->getLabel())
2100  .arg(ctx->subnet_->getID());
2101  }
2102 
2103  resp->setYiaddr(lease->addr_);
2104 
2109  if (!fake_allocation) {
2110  // If this is a renewing client it will set a ciaddr which the
2111  // server may include in the response. If this is a new allocation
2112  // the client will set ciaddr to 0 and this will also be propagated
2113  // to the server's resp.
2114  resp->setCiaddr(query->getCiaddr());
2115  }
2116 
2117  // We may need to update FQDN or hostname if the server is to generate
2118  // new name from the allocated IP address or if the allocation engine
2119  // has switched to a different subnet (from the same shared network)
2120  // where the client has hostname reservations.
2121  if (fqdn || opt_hostname) {
2122  bool should_update = false;
2123 
2124  // If there is a reservation in the current subnet for a hostname,
2125  // we need to use this reserved name.
2126  if (ctx->currentHost() && !ctx->currentHost()->getHostname().empty()) {
2127 
2128  lease->hostname_ = CfgMgr::instance().getD2ClientMgr()
2129  .qualifyName(ctx->currentHost()->getHostname(),
2130  static_cast<bool>(fqdn));
2131  should_update = true;
2132 
2133  // If there has been Client FQDN or Hostname option sent, but the
2134  // hostname is empty, it means that server is responsible for
2135  // generating the entire hostname for the client. The example of the
2136  // client's name, generated from the IP address is: host-192-0-2-3.
2137  } else if (lease->hostname_.empty()) {
2138 
2139  // Note that if we have received the hostname option, rather than
2140  // Client FQDN the trailing dot is not appended to the generated
2141  // hostname because some clients don't handle the trailing dot in
2142  // the hostname. Whether the trailing dot is appended or not is
2143  // controlled by the second argument to the generateFqdn().
2144  lease->hostname_ = CfgMgr::instance().getD2ClientMgr()
2145  .generateFqdn(lease->addr_, static_cast<bool>(fqdn));
2146 
2147  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_RESPONSE_HOSTNAME_GENERATE)
2148  .arg(query->getLabel())
2149  .arg(lease->hostname_);
2150 
2151  should_update = true;
2152  }
2153 
2154  if (should_update) {
2155 
2156  // The operations below are rather safe, but we want to catch
2157  // any potential exceptions (e.g. invalid lease database backend
2158  // implementation) and log an error.
2159  try {
2160  if (!fake_allocation) {
2161  // The lease update should be safe, because the lease should
2162  // be already in the database. In most cases the exception
2163  // would be thrown if the lease was missing.
2165  }
2166 
2167  // The name update in the option should be also safe,
2168  // because the generated name is well formed.
2169  if (fqdn) {
2170  fqdn->setDomainName(lease->hostname_,
2172  } else if (opt_hostname) {
2173  opt_hostname->setValue(lease->hostname_);
2174  }
2175 
2176  } catch (const Exception& ex) {
2177  LOG_ERROR(ddns4_logger, DHCP4_POST_ALLOCATION_NAME_UPDATE_FAIL)
2178  .arg(query->getLabel())
2179  .arg(lease->hostname_)
2180  .arg(ex.what());
2181  }
2182  }
2183  }
2184 
2185  // IP Address Lease time (type 51)
2187  lease->valid_lft_));
2188  resp->addOption(opt);
2189 
2190  // Subnet mask (type 1)
2191  resp->addOption(getNetmaskOption(subnet));
2192 
2193  // rebind timer (type 59) - if specified then send it only it if
2194  // it is less than lease lifetime. Note we "sanity" check T1
2195  // and T2 against lease lifetime here in event the lifetime has
2196  // been altered somewhere along the line.
2197  uint32_t timer_ceiling = lease->valid_lft_;
2198  if ((!subnet->getT2().unspecified()) &&
2199  (subnet->getT2() < timer_ceiling)) {
2202  subnet->getT2()));
2203  resp->addOption(t2);
2204 
2205  // If T2 is specified, then it becomes the ceiling for T1
2206  timer_ceiling = subnet->getT2();
2207  }
2208 
2209  // renewal-timer (type 58) - if specified then send it only if
2210  // it is less than the ceiling (T2 if given, lease life time if not)
2211  if ((!subnet->getT1().unspecified()) &&
2212  (subnet->getT1() < timer_ceiling)) {
2215  subnet->getT1()));
2216  resp->addOption(t1);
2217  }
2218 
2219 
2220  // Create NameChangeRequests if DDNS is enabled and this is a
2221  // real allocation.
2222  if (!fake_allocation && CfgMgr::instance().ddnsEnabled()) {
2223  try {
2224  LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_NCR_CREATE)
2225  .arg(query->getLabel());
2226  createNameChangeRequests(lease, ctx->old_lease_);
2227 
2228  } catch (const Exception& ex) {
2229  LOG_ERROR(ddns4_logger, DHCP4_NCR_CREATION_FAILED)
2230  .arg(query->getLabel())
2231  .arg(ex.what());
2232  }
2233  }
2234 
2235  } else {
2236  // Allocation engine did not allocate a lease. The engine logged
2237  // cause of that failure.
2238  LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, fake_allocation ?
2239  DHCP4_PACKET_NAK_0003 : DHCP4_PACKET_NAK_0004)
2240  .arg(query->getLabel())
2241  .arg(query->getCiaddr().toText())
2242  .arg(opt_requested_address ?
2243  opt_requested_address->readAddress().toText() : "(no address)");
2244 
2245  resp->setType(DHCPNAK);
2246  resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2247 
2248  resp->delOption(DHO_FQDN);
2249  resp->delOption(DHO_HOST_NAME);
2250  }
2251 }
2252 
2253 uint16_t
2255 
2256  // Look for a relay-port RAI sub-option in the query.
2257  const Pkt4Ptr& query = ex.getQuery();
2258  const OptionPtr& rai = query->getOption(DHO_DHCP_AGENT_OPTIONS);
2259  if (rai && rai->getOption(RAI_OPTION_RELAY_PORT)) {
2260  // Got the sub-option so use the remote port set by the relay.
2261  return (query->getRemotePort());
2262  }
2263  return (0);
2264 }
2265 
2266 void
2268  adjustRemoteAddr(ex);
2269 
2270  // Initialize the pointers to the client's message and the server's
2271  // response.
2272  Pkt4Ptr query = ex.getQuery();
2273  Pkt4Ptr response = ex.getResponse();
2274 
2275  // The DHCPINFORM is generally unicast to the client. The only situation
2276  // when the server is unable to unicast to the client is when the client
2277  // doesn't include ciaddr and the message is relayed. In this case the
2278  // server has to reply via relay agent. For other messages we send back
2279  // through relay if message is relayed, and unicast to the client if the
2280  // message is not relayed.
2281  // Note that the call to this function may throw if invalid combination
2282  // of hops and giaddr is found (hops = 0 if giaddr = 0 and hops != 0 if
2283  // giaddr != 0). The exception will propagate down and eventually cause the
2284  // packet to be discarded.
2285  if (((query->getType() == DHCPINFORM) &&
2286  ((!query->getCiaddr().isV4Zero()) ||
2287  (!query->isRelayed() && !query->getRemoteAddr().isV4Zero()))) ||
2288  ((query->getType() != DHCPINFORM) && !query->isRelayed())) {
2289  response->setRemotePort(DHCP4_CLIENT_PORT);
2290 
2291  } else {
2292  // RFC 8357 section 5.1
2293  uint16_t relay_port = checkRelayPort(ex);
2294  response->setRemotePort(relay_port ? relay_port : DHCP4_SERVER_PORT);
2295  }
2296 
2297  CfgIfacePtr cfg_iface = CfgMgr::instance().getCurrentCfg()->getCfgIface();
2298  if (query->isRelayed() &&
2299  (cfg_iface->getSocketType() == CfgIface::SOCKET_UDP) &&
2300  (cfg_iface->getOutboundIface() == CfgIface::USE_ROUTING)) {
2301 
2302  // Mark the response to follow routing
2303  response->setLocalAddr(IOAddress::IPV4_ZERO_ADDRESS());
2304  response->resetIndex();
2305  // But keep the interface name
2306  response->setIface(query->getIface());
2307 
2308  } else {
2309 
2310  IOAddress local_addr = query->getLocalAddr();
2311 
2312  // In many cases the query is sent to a broadcast address. This address
2313  // appears as a local address in the query message. We can't simply copy
2314  // this address to a response message and use it as a source address.
2315  // Instead we will need to use the address assigned to the interface
2316  // on which the query has been received. In other cases, we will just
2317  // use this address as a source address for the response.
2318  // Do the same for DHCPv4-over-DHCPv6 exchanges.
2319  if (local_addr.isV4Bcast() || query->isDhcp4o6()) {
2320  SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
2321  local_addr = sock_info.addr_;
2322  }
2323 
2324  // We assume that there is an appropriate socket bound to this address
2325  // and that the address is correct. This is safe assumption because
2326  // the local address of the query is set when the query is received.
2327  // The query sent to an incorrect address wouldn't have been received.
2328  // However, if socket is closed for this address between the reception
2329  // of the query and sending a response, the IfaceMgr should detect it
2330  // and return an error.
2331  response->setLocalAddr(local_addr);
2332  // In many cases the query is sent to a broadcast address. This address
2333  // appears as a local address in the query message. Therefore we can't
2334  // simply copy local address from the query and use it as a source
2335  // address for the response. Instead, we have to check what address our
2336  // socket is bound to and use it as a source address. This operation
2337  // may throw if for some reason the socket is closed.
2340  response->setIndex(query->getIndex());
2341  response->setIface(query->getIface());
2342  }
2343 
2344  response->setLocalPort(DHCP4_SERVER_PORT);
2345 }
2346 
2347 void
2349  // Initialize the pointers to the client's message and the server's
2350  // response.
2351  Pkt4Ptr query = ex.getQuery();
2352  Pkt4Ptr response = ex.getResponse();
2353 
2354  // DHCPv4-over-DHCPv6 is simple
2355  if (query->isDhcp4o6()) {
2356  response->setRemoteAddr(query->getRemoteAddr());
2357  return;
2358  }
2359 
2360  // The DHCPINFORM is slightly different than other messages in a sense
2361  // that the server should always unicast the response to the ciaddr.
2362  // It appears however that some clients don't set the ciaddr. We still
2363  // want to provision these clients and we do what we can't to send the
2364  // packet to the address where client can receive it.
2365  if (query->getType() == DHCPINFORM) {
2366  // If client adheres to RFC2131 it will set the ciaddr and in this
2367  // case we always unicast our response to this address.
2368  if (!query->getCiaddr().isV4Zero()) {
2369  response->setRemoteAddr(query->getCiaddr());
2370 
2371  // If we received DHCPINFORM via relay and the ciaddr is not set we
2372  // will try to send the response via relay. The caveat is that the
2373  // relay will not have any idea where to forward the packet because
2374  // the yiaddr is likely not set. So, the broadcast flag is set so
2375  // as the response may be broadcast.
2376  } else if (query->isRelayed()) {
2377  response->setRemoteAddr(query->getGiaddr());
2378  response->setFlags(response->getFlags() | BOOTP_BROADCAST);
2379 
2380  // If there is no ciaddr and no giaddr the only thing we can do is
2381  // to use the source address of the packet.
2382  } else {
2383  response->setRemoteAddr(query->getRemoteAddr());
2384  }
2385  // Remote address is now set so return.
2386  return;
2387  }
2388 
2389  // If received relayed message, server responds to the relay address.
2390  if (query->isRelayed()) {
2391  // The client should set the ciaddr when sending the DHCPINFORM
2392  // but in case he didn't, the relay may not be able to determine the
2393  // address of the client, because yiaddr is not set when responding
2394  // to Confirm and the only address available was the source address
2395  // of the client. The source address is however not used here because
2396  // the message is relayed. Therefore, we set the BROADCAST flag so
2397  // as the relay can broadcast the packet.
2398  if ((query->getType() == DHCPINFORM) &&
2399  query->getCiaddr().isV4Zero()) {
2400  response->setFlags(BOOTP_BROADCAST);
2401  }
2402  response->setRemoteAddr(query->getGiaddr());
2403 
2404  // If giaddr is 0 but client set ciaddr, server should unicast the
2405  // response to ciaddr.
2406  } else if (!query->getCiaddr().isV4Zero()) {
2407  response->setRemoteAddr(query->getCiaddr());
2408 
2409  // We can't unicast the response to the client when sending NAK,
2410  // because we haven't allocated address for him. Therefore,
2411  // NAK is broadcast.
2412  } else if (response->getType() == DHCPNAK) {
2413  response->setRemoteAddr(IOAddress::IPV4_BCAST_ADDRESS());
2414 
2415  // If yiaddr is set it means that we have created a lease for a client.
2416  } else if (!response->getYiaddr().isV4Zero()) {
2417  // If the broadcast bit is set in the flags field, we have to
2418  // send the response to broadcast address. Client may have requested it
2419  // because it doesn't support reception of messages on the interface
2420  // which doesn't have an address assigned. The other case when response
2421  // must be broadcasted is when our server does not support responding
2422  // directly to a client without address assigned.
2423  const bool bcast_flag = ((query->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
2424  if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
2425  response->setRemoteAddr(IOAddress::IPV4_BCAST_ADDRESS());
2426 
2427  // Client cleared the broadcast bit and we support direct responses
2428  // so we should unicast the response to a newly allocated address -
2429  // yiaddr.
2430  } else {
2431  response->setRemoteAddr(response ->getYiaddr());
2432 
2433  }
2434 
2435  // In most cases, we should have the remote address found already. If we
2436  // found ourselves at this point, the rational thing to do is to respond
2437  // to the address we got the query from.
2438  } else {
2439  response->setRemoteAddr(query->getRemoteAddr());
2440 
2441  }
2442 }
2443 
2444 void
2446  Pkt4Ptr query = ex.getQuery();
2447  Pkt4Ptr response = ex.getResponse();
2448 
2449  // Step 1: Start with fixed fields defined on subnet level.
2450  Subnet4Ptr subnet = ex.getContext()->subnet_;
2451  if (subnet) {
2452  IOAddress subnet_next_server = subnet->getSiaddr();
2453  if (!subnet_next_server.isV4Zero()) {
2454  response->setSiaddr(subnet_next_server);
2455  }
2456 
2457  const string& sname = subnet->getSname();
2458  if (!sname.empty()) {
2459  // Converting string to (const uint8_t*, size_t len) format is
2460  // tricky. reinterpret_cast is not the most elegant solution,
2461  // but it does avoid us making unnecessary copy. We will convert
2462  // sname and file fields in Pkt4 to string one day and life
2463  // will be easier.
2464  response->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
2465  sname.size());
2466  }
2467 
2468  const string& filename = subnet->getFilename();
2469  if (!filename.empty()) {
2470  // Converting string to (const uint8_t*, size_t len) format is
2471  // tricky. reinterpret_cast is not the most elegant solution,
2472  // but it does avoid us making unnecessary copy. We will convert
2473  // sname and file fields in Pkt4 to string one day and life
2474  // will be easier.
2475  response->setFile(reinterpret_cast<const uint8_t*>(filename.c_str()),
2476  filename.size());
2477  }
2478  }
2479 
2480  // Step 2: Try to set the values based on classes.
2481  // Any values defined in classes will override those from subnet level.
2482  const ClientClasses classes = query->getClasses();
2483  if (!classes.empty()) {
2484 
2485  // Let's get class definitions
2486  const ClientClassDictionaryPtr& dict =
2487  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
2488 
2489  // Now we need to iterate over the classes assigned to the
2490  // query packet and find corresponding class definitions for it.
2491  for (ClientClasses::const_iterator name = classes.cbegin();
2492  name != classes.cend(); ++name) {
2493 
2494  ClientClassDefPtr cl = dict->findClass(*name);
2495  if (!cl) {
2496  // Let's skip classes that don't have definitions. Currently
2497  // these are automatic classes VENDOR_CLASS_something, but there
2498  // may be other classes assigned under other circumstances, e.g.
2499  // by hooks.
2500  continue;
2501  }
2502 
2503  IOAddress next_server = cl->getNextServer();
2504  if (!next_server.isV4Zero()) {
2505  response->setSiaddr(next_server);
2506  }
2507 
2508  const string& sname = cl->getSname();
2509  if (!sname.empty()) {
2510  // Converting string to (const uint8_t*, size_t len) format is
2511  // tricky. reinterpret_cast is not the most elegant solution,
2512  // but it does avoid us making unnecessary copy. We will convert
2513  // sname and file fields in Pkt4 to string one day and life
2514  // will be easier.
2515  response->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
2516  sname.size());
2517  }
2518 
2519  const string& filename = cl->getFilename();
2520  if (!filename.empty()) {
2521  // Converting string to (const uint8_t*, size_t len) format is
2522  // tricky. reinterpret_cast is not the most elegant solution,
2523  // but it does avoid us making unnecessary copy. We will convert
2524  // sname and file fields in Pkt4 to string one day and life
2525  // will be easier.
2526  response->setFile(reinterpret_cast<const uint8_t*>(filename.c_str()),
2527  filename.size());
2528  }
2529  }
2530  }
2531 
2532  // Step 3: try to set values using HR. Any values coming from there will override
2533  // the subnet or class values.
2535 }
2536 
2537 OptionPtr
2538 Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
2539  uint32_t netmask = getNetmask4(subnet->get().second).toUint32();
2540 
2542  DHO_SUBNET_MASK, netmask));
2543 
2544  return (opt);
2545 }
2546 
2547 Pkt4Ptr
2549  sanityCheck(discover, FORBIDDEN);
2550 
2551  bool drop = false;
2552  Dhcpv4Exchange ex(alloc_engine_, discover, selectSubnet(discover, drop));
2553 
2554  // Stop here if selectSubnet decided to drop the packet
2555  if (drop) {
2556  return (Pkt4Ptr());
2557  }
2558 
2559  // If DHCPDISCOVER message contains the FQDN or Hostname option, server
2560  // may respond to the client with the appropriate FQDN or Hostname
2561  // option to indicate that whether it will take responsibility for
2562  // updating DNS when the client sends DHCPREQUEST message.
2563  processClientName(ex);
2564 
2565  assignLease(ex);
2566 
2567  if (!ex.getResponse()) {
2568  // The offer is empty so return it *now*!
2569  return (Pkt4Ptr());
2570  }
2571 
2572  // Adding any other options makes sense only when we got the lease.
2573  if (!ex.getResponse()->getYiaddr().isV4Zero()) {
2574  // Assign reserved classes.
2576  // Required classification
2577  requiredClassify(ex);
2578 
2579  buildCfgOptionList(ex);
2582  // There are a few basic options that we always want to
2583  // include in the response. If client did not request
2584  // them we append them for him.
2585  appendBasicOptions(ex);
2586 
2587  // Set fixed fields (siaddr, sname, filename) if defined in
2588  // the reservation, class or subnet specific configuration.
2589  setFixedFields(ex);
2590 
2591  } else {
2592  // If the server can't offer an address, it drops the packet.
2593  return (Pkt4Ptr());
2594 
2595  }
2596 
2597  // Set the src/dest IP address, port and interface for the outgoing
2598  // packet.
2599  adjustIfaceData(ex);
2600 
2601  appendServerID(ex);
2602 
2603  return (ex.getResponse());
2604 }
2605 
2606 Pkt4Ptr
2610 
2611  bool drop = false;
2612  Dhcpv4Exchange ex(alloc_engine_, request, selectSubnet(request, drop));
2613 
2614  // Stop here if selectSubnet decided to drop the packet
2615  if (drop) {
2616  return (Pkt4Ptr());
2617  }
2618 
2619  // If DHCPREQUEST message contains the FQDN or Hostname option, server
2620  // should respond to the client with the appropriate FQDN or Hostname
2621  // option to indicate if it takes responsibility for the DNS updates.
2622  // This is performed by the function below.
2623  processClientName(ex);
2624 
2625  // Note that we treat REQUEST message uniformly, regardless if this is a
2626  // first request (requesting for new address), renewing existing address
2627  // or even rebinding.
2628  assignLease(ex);
2629 
2630  if (!ex.getResponse()) {
2631  // The ack is empty so return it *now*!
2632  return (Pkt4Ptr());
2633  }
2634 
2635  // Adding any other options makes sense only when we got the lease.
2636  if (!ex.getResponse()->getYiaddr().isV4Zero()) {
2637  // Assign reserved classes.
2639  // Required classification
2640  requiredClassify(ex);
2641 
2642  buildCfgOptionList(ex);
2645  // There are a few basic options that we always want to
2646  // include in the response. If client did not request
2647  // them we append them for him.
2648  appendBasicOptions(ex);
2649 
2650  // Set fixed fields (siaddr, sname, filename) if defined in
2651  // the reservation, class or subnet specific configuration.
2652  setFixedFields(ex);
2653  }
2654 
2655  // Set the src/dest IP address, port and interface for the outgoing
2656  // packet.
2657  adjustIfaceData(ex);
2658 
2659  appendServerID(ex);
2660 
2661  // Return the pointer to the context, which will be required by the
2662  // leases4_comitted callouts.
2663  context = ex.getContext();
2664 
2665  return (ex.getResponse());
2666 }
2667 
2668 void
2672 
2673  // Try to find client-id. Note that for the DHCPRELEASE we don't check if the
2674  // match-client-id configuration parameter is disabled because this parameter
2675  // is configured for subnets and we don't select subnet for the DHCPRELEASE.
2676  // Bogus clients usually generate new client identifiers when they first
2677  // connect to the network, so whatever client identifier has been used to
2678  // acquire the lease, the client identifier carried in the DHCPRELEASE is
2679  // likely to be the same and the lease will be correctly identified in the
2680  // lease database. If supplied client identifier differs from the one used
2681  // to acquire the lease then the lease will remain in the database and
2682  // simply expire.
2683  ClientIdPtr client_id;
2684  OptionPtr opt = release->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
2685  if (opt) {
2686  client_id = ClientIdPtr(new ClientId(opt->getData()));
2687  }
2688 
2689  try {
2690  // Do we have a lease for that particular address?
2691  Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getCiaddr());
2692 
2693  if (!lease) {
2694  // No such lease - bogus release
2695  LOG_DEBUG(lease4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_NO_LEASE)
2696  .arg(release->getLabel())
2697  .arg(release->getCiaddr().toText());
2698  return;
2699  }
2700 
2701  if (!lease->belongsToClient(release->getHWAddr(), client_id)) {
2702  LOG_DEBUG(lease4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_CLIENT)
2703  .arg(release->getLabel())
2704  .arg(release->getCiaddr().toText());
2705  return;
2706  }
2707 
2708  bool skip = false;
2709 
2710  // Execute all callouts registered for lease4_release
2711  if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_release_)) {
2712  CalloutHandlePtr callout_handle = getCalloutHandle(release);
2713 
2714  // Use the RAII wrapper to make sure that the callout handle state is
2715  // reset when this object goes out of scope. All hook points must do
2716  // it to prevent possible circular dependency between the callout
2717  // handle and its arguments.
2718  ScopedCalloutHandleState callout_handle_state(callout_handle);
2719 
2720  // Enable copying options from the packet within hook library.
2721  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(release);
2722 
2723  // Pass the original packet
2724  callout_handle->setArgument("query4", release);
2725 
2726  // Pass the lease to be updated
2727  callout_handle->setArgument("lease4", lease);
2728 
2729  // Call all installed callouts
2730  HooksManager::callCallouts(Hooks.hook_index_lease4_release_,
2731  *callout_handle);
2732 
2733  // Callouts decided to skip the next processing step. The next
2734  // processing step would to send the packet, so skip at this
2735  // stage means "drop response".
2736  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
2737  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
2738  skip = true;
2740  DHCP4_HOOK_LEASE4_RELEASE_SKIP)
2741  .arg(release->getLabel());
2742  }
2743  }
2744 
2745  // Callout didn't indicate to skip the release process. Let's release
2746  // the lease.
2747  if (!skip) {
2748  bool success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
2749 
2750  if (success) {
2751 
2752  context.reset(new AllocEngine::ClientContext4());
2753  context->old_lease_ = lease;
2754 
2755  // Release successful
2756  LOG_INFO(lease4_logger, DHCP4_RELEASE)
2757  .arg(release->getLabel())
2758  .arg(lease->addr_.toText());
2759 
2760  // Need to decrease statistic for assigned addresses.
2761  StatsMgr::instance().addValue(
2762  StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-addresses"),
2763  static_cast<int64_t>(-1));
2764 
2765  // Remove existing DNS entries for the lease, if any.
2766  queueNCR(CHG_REMOVE, lease);
2767 
2768  } else {
2769  // Release failed
2770  LOG_ERROR(lease4_logger, DHCP4_RELEASE_FAIL)
2771  .arg(release->getLabel())
2772  .arg(lease->addr_.toText());
2773  }
2774  }
2775  } catch (const isc::Exception& ex) {
2776  LOG_ERROR(lease4_logger, DHCP4_RELEASE_EXCEPTION)
2777  .arg(release->getLabel())
2778  .arg(release->getCiaddr())
2779  .arg(ex.what());
2780  }
2781 }
2782 
2783 void
2785 
2786  // Server-id is mandatory in DHCPDECLINE (see table 5, RFC2131)
2788  // sanityCheck(decline, MANDATORY);
2789 
2790  // Client is supposed to specify the address being declined in
2791  // Requested IP address option, but must not set its ciaddr.
2792  // (again, see table 5 in RFC2131).
2793 
2794  OptionCustomPtr opt_requested_address = boost::dynamic_pointer_cast<
2795  OptionCustom>(decline->getOption(DHO_DHCP_REQUESTED_ADDRESS));
2796  if (!opt_requested_address) {
2797 
2798  isc_throw(RFCViolation, "Mandatory 'Requested IP address' option missing"
2799  "in DHCPDECLINE sent from " << decline->getLabel());
2800  }
2801  IOAddress addr(opt_requested_address->readAddress());
2802 
2803  // We could also extract client's address from ciaddr, but that's clearly
2804  // against RFC2131.
2805 
2806  // Now we need to check whether this address really belongs to the client
2807  // that attempts to decline it.
2808  const Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(addr);
2809 
2810  if (!lease) {
2811  // Client tried to decline an address, but we don't have a lease for
2812  // that address. Let's ignore it.
2813  //
2814  // We could assume that we're recovering from a mishandled migration
2815  // to a new server and mark the address as declined, but the window of
2816  // opportunity for that to be useful is small and the attack vector
2817  // would be pretty severe.
2818  LOG_WARN(dhcp4_logger, DHCP4_DECLINE_LEASE_NOT_FOUND)
2819  .arg(addr.toText()).arg(decline->getLabel());
2820  return;
2821  }
2822 
2823  // Get client-id, if available.
2824  OptionPtr opt_clientid = decline->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
2825  ClientIdPtr client_id;
2826  if (opt_clientid) {
2827  client_id.reset(new ClientId(opt_clientid->getData()));
2828  }
2829 
2830  // Check if the client attempted to decline a lease it doesn't own.
2831  if (!lease->belongsToClient(decline->getHWAddr(), client_id)) {
2832 
2833  // Get printable hardware addresses
2834  string client_hw = decline->getHWAddr() ?
2835  decline->getHWAddr()->toText(false) : "(none)";
2836  string lease_hw = lease->hwaddr_ ?
2837  lease->hwaddr_->toText(false) : "(none)";
2838 
2839  // Get printable client-ids
2840  string client_id_txt = client_id ? client_id->toText() : "(none)";
2841  string lease_id_txt = lease->client_id_ ?
2842  lease->client_id_->toText() : "(none)";
2843 
2844  // Print the warning and we're done here.
2845  LOG_WARN(dhcp4_logger, DHCP4_DECLINE_LEASE_MISMATCH)
2846  .arg(addr.toText()).arg(decline->getLabel())
2847  .arg(client_hw).arg(lease_hw).arg(client_id_txt).arg(lease_id_txt);
2848 
2849  return;
2850  }
2851 
2852  // Ok, all is good. The client is reporting its own address. Let's
2853  // process it.
2854  declineLease(lease, decline, context);
2855 }
2856 
2857 void
2858 Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline,
2859  AllocEngine::ClientContext4Ptr& context) {
2860 
2861  // Let's check if there are hooks installed for decline4 hook point.
2862  // If they are, let's pass the lease and client's packet. If the hook
2863  // sets status to drop, we reject this Decline.
2864  if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_decline_)) {
2865  CalloutHandlePtr callout_handle = getCalloutHandle(decline);
2866 
2867  // Use the RAII wrapper to make sure that the callout handle state is
2868  // reset when this object goes out of scope. All hook points must do
2869  // it to prevent possible circular dependency between the callout
2870  // handle and its arguments.
2871  ScopedCalloutHandleState callout_handle_state(callout_handle);
2872 
2873  // Enable copying options from the packet within hook library.
2874  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(decline);
2875 
2876  // Pass incoming Decline and the lease to be declined.
2877  callout_handle->setArgument("lease4", lease);
2878  callout_handle->setArgument("query4", decline);
2879 
2880  // Call callouts
2881  HooksManager::callCallouts(Hooks.hook_index_lease4_decline_,
2882  *callout_handle);
2883 
2884  // Check if callouts decided to skip the next processing step.
2885  // If any of them did, we will drop the packet.
2886  if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
2887  (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
2888  LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_DECLINE_SKIP)
2889  .arg(decline->getLabel()).arg(lease->addr_.toText());
2890  return;
2891  }
2892  }
2893 
2894  // Remove existing DNS entries for the lease, if any.
2895  // queueNCR will do the necessary checks and will skip the update, if not needed.
2896  queueNCR(CHG_REMOVE, lease);
2897 
2898  // Bump up the statistics.
2899 
2900  // Per subnet declined addresses counter.
2901  StatsMgr::instance().addValue(
2902  StatsMgr::generateName("subnet", lease->subnet_id_, "declined-addresses"),
2903  static_cast<int64_t>(1));
2904 
2905  // Global declined addresses counter.
2906  StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
2907 
2908  // We do not want to decrease the assigned-addresses at this time. While
2909  // technically a declined address is no longer allocated, the primary usage
2910  // of the assigned-addresses statistic is to monitor pool utilization. Most
2911  // people would forget to include declined-addresses in the calculation,
2912  // and simply do assigned-addresses/total-addresses. This would have a bias
2913  // towards under-representing pool utilization, if we decreased allocated
2914  // immediately after receiving DHCPDECLINE, rather than later when we recover
2915  // the address.
2916 
2917  // @todo: Call hooks.
2918 
2919  // We need to disassociate the lease from the client. Once we move a lease
2920  // to declined state, it is no longer associated with the client in any
2921  // way.
2922  lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
2923 
2925 
2926  context.reset(new AllocEngine::ClientContext4());
2927  context->new_lease_ = lease;
2928 
2929  LOG_INFO(lease4_logger, DHCP4_DECLINE_LEASE).arg(lease->addr_.toText())
2930  .arg(decline->getLabel()).arg(lease->valid_lft_);
2931 }
2932 
2933 Pkt4Ptr
2935  // DHCPINFORM MUST not include server identifier.
2936  sanityCheck(inform, FORBIDDEN);
2937 
2938  bool drop = false;
2939  Dhcpv4Exchange ex(alloc_engine_, inform, selectSubnet(inform, drop));
2940 
2941  // Stop here if selectSubnet decided to drop the packet
2942  if (drop) {
2943  return (Pkt4Ptr());
2944  }
2945 
2946  Pkt4Ptr ack = ex.getResponse();
2947 
2949  requiredClassify(ex);
2950 
2951  buildCfgOptionList(ex);
2954  appendBasicOptions(ex);
2955  adjustIfaceData(ex);
2956 
2957  // Set fixed fields (siaddr, sname, filename) if defined in
2958  // the reservation, class or subnet specific configuration.
2959  setFixedFields(ex);
2960 
2961  // There are cases for the DHCPINFORM that the server receives it via
2962  // relay but will send the response to the client's unicast address
2963  // carried in the ciaddr. In this case, the giaddr and hops field should
2964  // be cleared (these fields were copied by the copyDefaultFields function).
2965  // Also Relay Agent Options should be removed if present.
2966  if (ack->getRemoteAddr() != inform->getGiaddr()) {
2967  LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_INFORM_DIRECT_REPLY)
2968  .arg(inform->getLabel())
2969  .arg(ack->getRemoteAddr())
2970  .arg(ack->getIface());
2971  ack->setHops(0);
2972  ack->setGiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2973  ack->delOption(DHO_DHCP_AGENT_OPTIONS);
2974  }
2975 
2976  // The DHCPACK must contain server id.
2977  appendServerID(ex);
2978 
2979  return (ex.getResponse());
2980 }
2981 
2982 bool
2983 Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
2984  // Check that the message type is accepted by the server. We rely on the
2985  // function called to log a message if needed.
2986  if (!acceptMessageType(query)) {
2987  return (false);
2988  }
2989  // Check if the message from directly connected client (if directly
2990  // connected) should be dropped or processed.
2991  if (!acceptDirectRequest(query)) {
2992  LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0002)
2993  .arg(query->getLabel())
2994  .arg(query->getIface());
2995  return (false);
2996  }
2997 
2998  // Check if the DHCPv4 packet has been sent to us or to someone else.
2999  // If it hasn't been sent to us, drop it!
3000  if (!acceptServerId(query)) {
3001  LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0003)
3002  .arg(query->getLabel())
3003  .arg(query->getIface());
3004  return (false);
3005  }
3006 
3007  return (true);
3008 }
3009 
3010 bool
3012  // Accept all relayed messages.
3013  if (pkt->isRelayed()) {
3014  return (true);
3015  }
3016 
3017  // Accept all DHCPv4-over-DHCPv6 messages.
3018  if (pkt->isDhcp4o6()) {
3019  return (true);
3020  }
3021 
3022  // The source address must not be zero for the DHCPINFORM message from
3023  // the directly connected client because the server will not know where
3024  // to respond if the ciaddr was not present.
3025  try {
3026  if (pkt->getType() == DHCPINFORM) {
3027  if (pkt->getRemoteAddr().isV4Zero() &&
3028  pkt->getCiaddr().isV4Zero()) {
3029  return (false);
3030  }
3031  }
3032  } catch (...) {
3033  // If we got here, it is probably because the message type hasn't
3034  // been set. But, this should not really happen assuming that
3035  // we validate the message type prior to calling this function.
3036  return (false);
3037  }
3038  bool drop = false;
3039  bool result = (!pkt->getLocalAddr().isV4Bcast() ||
3040  selectSubnet(pkt, drop, true));
3041  if (drop) {
3042  // The packet must be dropped but as sanity_only is true it is dead code.
3043  return (false);
3044  }
3045  return (result);
3046 }
3047 
3048 bool
3050  // When receiving a packet without message type option, getType() will
3051  // throw.
3052  int type;
3053  try {
3054  type = query->getType();
3055 
3056  } catch (...) {
3057  LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0004)
3058  .arg(query->getLabel())
3059  .arg(query->getIface());
3060  return (false);
3061  }
3062 
3063  // Once we know that the message type is within a range of defined DHCPv4
3064  // messages, we do a detailed check to make sure that the received message
3065  // is targeted at server. Note that we could have received some Offer
3066  // message broadcasted by the other server to a relay. Even though, the
3067  // server would rather unicast its response to a relay, let's be on the
3068  // safe side. Also, we want to drop other messages which we don't support.
3069  // All these valid messages that we are not going to process are dropped
3070  // silently.
3071 
3072  switch(type) {
3073  case DHCPDISCOVER:
3074  case DHCPREQUEST:
3075  case DHCPRELEASE:
3076  case DHCPDECLINE:
3077  case DHCPINFORM:
3078  return (true);
3079  break;
3080 
3081  case DHCP_NOTYPE:
3082  LOG_INFO(bad_packet4_logger, DHCP4_PACKET_DROP_0009)
3083  .arg(query->getLabel());
3084  break;
3085 
3086  default:
3087  // If we receive a message with a non-existing type, we are logging it.
3088  if (type >= DHCP_TYPES_EOF) {
3089  LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0005)
3090  .arg(query->getLabel())
3091  .arg(type);
3092  } else {
3093  // Exists but we don't support it.
3094  LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0006)
3095  .arg(query->getLabel())
3096  .arg(type);
3097  }
3098  break;
3099  }
3100 
3101  return (false);
3102 }
3103 
3104 bool
3105 Dhcpv4Srv::acceptServerId(const Pkt4Ptr& query) const {
3106  // This function is meant to be called internally by the server class, so
3107  // we rely on the caller to sanity check the pointer and we don't check
3108  // it here.
3109 
3110  // Check if server identifier option is present. If it is not present
3111  // we accept the message because it is targeted to all servers.
3112  // Note that we don't check cases that server identifier is mandatory
3113  // but not present. This is meant to be sanity checked in other
3114  // functions.
3115  OptionPtr option = query->getOption(DHO_DHCP_SERVER_IDENTIFIER);
3116  if (!option) {
3117  return (true);
3118  }
3119  // Server identifier is present. Let's convert it to 4-byte address
3120  // and try to match with server identifiers used by the server.
3121  OptionCustomPtr option_custom =
3122  boost::dynamic_pointer_cast<OptionCustom>(option);
3123  // Unable to convert the option to the option type which encapsulates it.
3124  // We treat this as non-matching server id.
3125  if (!option_custom) {
3126  return (false);
3127  }
3128  // The server identifier option should carry exactly one IPv4 address.
3129  // If the option definition for the server identifier doesn't change,
3130  // the OptionCustom object should have exactly one IPv4 address and
3131  // this check is somewhat redundant. On the other hand, if someone
3132  // breaks option it may be better to check that here.
3133  if (option_custom->getDataFieldsNum() != 1) {
3134  return (false);
3135  }
3136 
3137  // The server identifier MUST be an IPv4 address. If given address is
3138  // v6, it is wrong.
3139  IOAddress server_id = option_custom->readAddress();
3140  if (!server_id.isV4()) {
3141  return (false);
3142  }
3143 
3144  // This function iterates over all interfaces on which the
3145  // server is listening to find the one which has a socket bound
3146  // to the address carried in the server identifier option.
3147  // This has some performance implications. However, given that
3148  // typically there will be just a few active interfaces the
3149  // performance hit should be acceptable. If it turns out to
3150  // be significant, we will have to cache server identifiers
3151  // when sockets are opened.
3152  if (IfaceMgr::instance().hasOpenSocket(server_id)) {
3153  return (true);
3154  }
3155 
3156  // There are some cases when an administrator explicitly sets server
3157  // identifier (option 54) that should be used for a given, subnet,
3158  // network etc. It doesn't have to be an address assigned to any of
3159  // the server interfaces. Thus, we have to check if the server
3160  // identifier received is the one that we explicitly set in the
3161  // server configuration. At this point, we don't know which subnet
3162  // the client belongs to so we can't match the server id with any
3163  // subnet. We simply check if this server identifier is configured
3164  // anywhere. This should be good enough to eliminate exchanges
3165  // with other servers in the same network.
3166 
3175 
3177 
3178  // Check if there is at least one subnet configured with this server
3179  // identifier.
3180  ConstCfgSubnets4Ptr cfg_subnets = cfg->getCfgSubnets4();
3181  if (cfg_subnets->hasSubnetWithServerId(server_id)) {
3182  return (true);
3183  }
3184 
3185  // This server identifier is not configured for any of the subnets, so
3186  // check on the shared network level.
3187  CfgSharedNetworks4Ptr cfg_networks = cfg->getCfgSharedNetworks4();
3188  if (cfg_networks->hasNetworkWithServerId(server_id)) {
3189  return (true);
3190  }
3191 
3192  // Finally, it is possible that the server identifier is specified
3193  // on the global level.
3194  ConstCfgOptionPtr cfg_global_options = cfg->getCfgOption();
3195  OptionCustomPtr opt_server_id = boost::dynamic_pointer_cast<OptionCustom>
3196  (cfg_global_options->get(DHCP4_OPTION_SPACE, DHO_DHCP_SERVER_IDENTIFIER).option_);
3197 
3198  return (opt_server_id && (opt_server_id->readAddress() == server_id));
3199 }
3200 
3201 void
3203  OptionPtr server_id = query->getOption(DHO_DHCP_SERVER_IDENTIFIER);
3204  switch (serverid) {
3205  case FORBIDDEN:
3206  if (server_id) {
3207  isc_throw(RFCViolation, "Server-id option was not expected, but "
3208  << "received in "
3209  << query->getName());
3210  }
3211  break;
3212 
3213  case MANDATORY:
3214  if (!server_id) {
3215  isc_throw(RFCViolation, "Server-id option was expected, but not "
3216  " received in message "
3217  << query->getName());
3218  }
3219  break;
3220 
3221  case OPTIONAL:
3222  // do nothing here
3223  ;
3224  }
3225 
3226  // If there is HWAddress set and it is non-empty, then we're good
3227  if (query->getHWAddr() && !query->getHWAddr()->hwaddr_.empty()) {
3228  return;
3229  }
3230 
3231  // There has to be something to uniquely identify the client:
3232  // either non-zero MAC address or client-id option present (or both)
3233  OptionPtr client_id = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
3234 
3235  // If there's no client-id (or a useless one is provided, i.e. 0 length)
3236  if (!client_id || client_id->len() == client_id->getHeaderLen()) {
3237  isc_throw(RFCViolation, "Missing or useless client-id and no HW address "
3238  " provided in message "
3239  << query->getName());
3240  }
3241 }
3242 
3244  // Built-in vendor class processing
3245  boost::shared_ptr<OptionString> vendor_class =
3246  boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER));
3247 
3248  if (!vendor_class) {
3249  return;
3250  }
3251 
3252  pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
3253 }
3254 
3256  // All packets belongs to ALL.
3257  pkt->addClass("ALL");
3258 
3259  // First: built-in vendor class processing.
3260  classifyByVendor(pkt);
3261 
3262  // Run match expressions on classes not depending on KNOWN/UNKNOWN.
3263  evaluateClasses(pkt, false);
3264 }
3265 
3266 void Dhcpv4Srv::evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known) {
3267  // Note getClientClassDictionary() cannot be null
3268  const ClientClassDictionaryPtr& dict =
3269  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3270  const ClientClassDefListPtr& defs_ptr = dict->getClasses();
3271  for (ClientClassDefList::const_iterator it = defs_ptr->cbegin();
3272  it != defs_ptr->cend(); ++it) {
3273  // Note second cannot be null
3274  const ExpressionPtr& expr_ptr = (*it)->getMatchExpr();
3275  // Nothing to do without an expression to evaluate
3276  if (!expr_ptr) {
3277  continue;
3278  }
3279  // Not the right time if only when required
3280  if ((*it)->getRequired()) {
3281  continue;
3282  }
3283  // Not the right pass.
3284  if ((*it)->getDependOnKnown() != depend_on_known) {
3285  continue;
3286  }
3287  // Evaluate the expression which can return false (no match),
3288  // true (match) or raise an exception (error)
3289  try {
3290  bool status = evaluateBool(*expr_ptr, *pkt);
3291  if (status) {
3292  LOG_INFO(options4_logger, EVAL_RESULT)
3293  .arg((*it)->getName())
3294  .arg(status);
3295  // Matching: add the class
3296  pkt->addClass((*it)->getName());
3297  } else {
3299  .arg((*it)->getName())
3300  .arg(status);
3301  }
3302  } catch (const Exception& ex) {
3303  LOG_ERROR(options4_logger, EVAL_RESULT)
3304  .arg((*it)->getName())
3305  .arg(ex.what());
3306  } catch (...) {
3307  LOG_ERROR(options4_logger, EVAL_RESULT)
3308  .arg((*it)->getName())
3309  .arg("get exception?");
3310  }
3311  }
3312 }
3313 
3315  // First collect required classes
3316  Pkt4Ptr query = ex.getQuery();
3317  ClientClasses classes = query->getClasses(true);
3318  Subnet4Ptr subnet = ex.getContext()->subnet_;
3319 
3320  if (subnet) {
3321  // Begin by the shared-network
3322  SharedNetwork4Ptr network;
3323  subnet->getSharedNetwork(network);
3324  if (network) {
3325  const ClientClasses& to_add = network->getRequiredClasses();
3326  for (ClientClasses::const_iterator cclass = to_add.cbegin();
3327  cclass != to_add.cend(); ++cclass) {
3328  classes.insert(*cclass);
3329  }
3330  }
3331 
3332  // Followed by the subnet
3333  const ClientClasses& to_add = subnet->getRequiredClasses();
3334  for(ClientClasses::const_iterator cclass = to_add.cbegin();
3335  cclass != to_add.cend(); ++cclass) {
3336  classes.insert(*cclass);
3337  }
3338 
3339  // And finish by the pool
3340  Pkt4Ptr resp = ex.getResponse();
3342  if (resp) {
3343  addr = resp->getYiaddr();
3344  }
3345  if (!addr.isV4Zero()) {
3346  PoolPtr pool = subnet->getPool(Lease::TYPE_V4, addr, false);
3347  if (pool) {
3348  const ClientClasses& to_add = pool->getRequiredClasses();
3349  for (ClientClasses::const_iterator cclass = to_add.cbegin();
3350  cclass != to_add.cend(); ++cclass) {
3351  classes.insert(*cclass);
3352  }
3353  }
3354  }
3355 
3356  // host reservation???
3357  }
3358 
3359  // Run match expressions
3360  // Note getClientClassDictionary() cannot be null
3361  const ClientClassDictionaryPtr& dict =
3362  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3363  for (ClientClasses::const_iterator cclass = classes.cbegin();
3364  cclass != classes.cend(); ++cclass) {
3365  const ClientClassDefPtr class_def = dict->findClass(*cclass);
3366  if (!class_def) {
3367  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNDEFINED)
3368  .arg(*cclass);
3369  continue;
3370  }
3371  const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
3372  // Nothing to do without an expression to evaluate
3373  if (!expr_ptr) {
3374  LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNTESTABLE)
3375  .arg(*cclass);
3376  continue;
3377  }
3378  // Evaluate the expression which can return false (no match),
3379  // true (match) or raise an exception (error)
3380  try {
3381  bool status = evaluateBool(*expr_ptr, *query);
3382  if (status) {
3383  LOG_INFO(options4_logger, EVAL_RESULT)
3384  .arg(*cclass)
3385  .arg(status);
3386  // Matching: add the class
3387  query->addClass(*cclass);
3388  } else {
3390  .arg(*cclass)
3391  .arg(status);
3392  }
3393  } catch (const Exception& ex) {
3394  LOG_ERROR(options4_logger, EVAL_RESULT)
3395  .arg(*cclass)
3396  .arg(ex.what());
3397  } catch (...) {
3398  LOG_ERROR(options4_logger, EVAL_RESULT)
3399  .arg(*cclass)
3400  .arg("get exception?");
3401  }
3402  }
3403 }
3404 
3405 void
3407 {
3408  // Iterate on the list of deferred option codes
3409  BOOST_FOREACH(const uint16_t& code, query->getDeferredOptions()) {
3410  OptionDefinitionPtr def;
3411  // Iterate on client classes
3412  const ClientClasses& classes = query->getClasses();
3413  for (ClientClasses::const_iterator cclass = classes.cbegin();
3414  cclass != classes.cend(); ++cclass) {
3415  // Get the client class definition for this class
3416  const ClientClassDefPtr& ccdef =
3418  getClientClassDictionary()->findClass(*cclass);
3419  // If not found skip it
3420  if (!ccdef) {
3421  continue;
3422  }
3423  // If there is no option definition skip it
3424  if (!ccdef->getCfgOptionDef()) {
3425  continue;
3426  }
3427  def = ccdef->getCfgOptionDef()->get(DHCP4_OPTION_SPACE, code);
3428  // Stop at the first client class with a defition
3429  if (def) {
3430  break;
3431  }
3432  }
3433  // If not found try the global definition
3434  if (!def) {
3436  }
3437  if (!def) {
3439  }
3440  // Finish by last resort definition
3441  if (!def) {
3443  }
3444  // If not defined go to the next option
3445  if (!def) {
3446  continue;
3447  }
3448  // Get the existing option for its content and remove all
3449  OptionPtr opt = query->getOption(code);
3450  if (!opt) {
3451  // should not happen but do not crash anyway
3452  continue;
3453  }
3454  const OptionBuffer buf = opt->getData();
3455  while (query->delOption(code)) {
3456  // continue
3457  }
3458  // Unpack the option and add it
3459  opt = def->optionFactory(Option::V4, code, buf.cbegin(), buf.cend());
3460  query->addOption(opt);
3461  }
3462 }
3463 
3464 void
3467  if (d2_mgr.ddnsEnabled()) {
3468  // Updates are enabled, so lets start the sender, passing in
3469  // our error handler.
3470  // This may throw so wherever this is called needs to ready.
3471  d2_mgr.startSender(boost::bind(&Dhcpv4Srv::d2ClientErrorHandler,
3472  this, _1, _2));
3473  }
3474 }
3475 
3476 void
3479  if (d2_mgr.ddnsEnabled()) {
3480  // Updates are enabled, so lets stop the sender
3481  d2_mgr.stopSender();
3482  }
3483 }
3484 
3485 void
3489  LOG_ERROR(ddns4_logger, DHCP4_DDNS_REQUEST_SEND_FAILED).
3490  arg(result).arg((ncr ? ncr->toText() : " NULL "));
3491  // We cannot communicate with kea-dhcp-ddns, suspend further updates.
3495 }
3496 
3497 // Refer to config_report so it will be embedded in the binary
3499 
3500 std::string
3501 Dhcpv4Srv::getVersion(bool extended) {
3502  std::stringstream tmp;
3503 
3504  tmp << VERSION;
3505  if (extended) {
3506  tmp << endl << EXTENDED_VERSION << endl;
3507  tmp << "linked with:" << endl;
3508  tmp << Logger::getVersion() << endl;
3509  tmp << CryptoLink::getVersion() << endl;
3510  tmp << "database:" << endl;
3511 #ifdef HAVE_MYSQL
3512  tmp << MySqlLeaseMgr::getDBVersion() << endl;
3513 #endif
3514 #ifdef HAVE_PGSQL
3515  tmp << PgSqlLeaseMgr::getDBVersion() << endl;
3516 #endif
3517 #ifdef HAVE_CQL
3518  tmp << CqlLeaseMgr::getDBVersion() << endl;
3519 #endif
3521 
3522  // @todo: more details about database runtime
3523  }
3524 
3525  return (tmp.str());
3526 }
3527 
3529  // Note that we're not bumping pkt4-received statistic as it was
3530  // increased early in the packet reception code.
3531 
3532  string stat_name = "pkt4-unknown-received";
3533  try {
3534  switch (query->getType()) {
3535  case DHCPDISCOVER:
3536  stat_name = "pkt4-discover-received";
3537  break;
3538  case DHCPOFFER:
3539  // Should not happen, but let's keep a counter for it
3540  stat_name = "pkt4-offer-received";
3541  break;
3542  case DHCPREQUEST:
3543  stat_name = "pkt4-request-received";
3544  break;
3545  case DHCPACK:
3546  // Should not happen, but let's keep a counter for it
3547  stat_name = "pkt4-ack-received";
3548  break;
3549  case DHCPNAK:
3550  // Should not happen, but let's keep a counter for it
3551  stat_name = "pkt4-nak-received";
3552  break;
3553  case DHCPRELEASE:
3554  stat_name = "pkt4-release-received";
3555  break;
3556  case DHCPDECLINE:
3557  stat_name = "pkt4-decline-received";
3558  break;
3559  case DHCPINFORM:
3560  stat_name = "pkt4-inform-received";
3561  break;
3562  default:
3563  ; // do nothing
3564  }
3565  }
3566  catch (...) {
3567  // If the incoming packet doesn't have option 53 (message type)
3568  // or a hook set pkt4_receive_skip, then Pkt4::getType() may
3569  // throw an exception. That's ok, we'll then use the default
3570  // name of pkt4-unknown-received.
3571  }
3572 
3574  static_cast<int64_t>(1));
3575 }
3576 
3577 void Dhcpv4Srv::processStatsSent(const Pkt4Ptr& response) {
3578  // Increase generic counter for sent packets.
3580  static_cast<int64_t>(1));
3581 
3582  // Increase packet type specific counter for packets sent.
3583  string stat_name;
3584  switch (response->getType()) {
3585  case DHCPOFFER:
3586  stat_name = "pkt4-offer-sent";
3587  break;
3588  case DHCPACK:
3589  stat_name = "pkt4-ack-sent";
3590  break;
3591  case DHCPNAK:
3592  stat_name = "pkt4-nak-sent";
3593  break;
3594  default:
3595  // That should never happen
3596  return;
3597  }
3598 
3600  static_cast<int64_t>(1));
3601 }
3602 
3604  return (Hooks.hook_index_buffer4_receive_);
3605 }
3606 
3608  return (Hooks.hook_index_pkt4_receive_);
3609 }
3610 
3612  return (Hooks.hook_index_subnet4_select_);
3613 }
3614 
3616  return (Hooks.hook_index_lease4_release_);
3617 }
3618 
3620  return (Hooks.hook_index_pkt4_send_);
3621 }
3622 
3624  return (Hooks.hook_index_buffer4_send_);
3625 }
3626 
3628  return (Hooks.hook_index_lease4_decline_);
3629 }
3630 
3632  // Clear any packets held by the callhout handle store and
3633  // all parked packets
3634  isc::dhcp::Pkt4Ptr pkt4ptr_empty;
3635  isc::dhcp::getCalloutHandle(pkt4ptr_empty);
3636  HooksManager::clearParkingLots();
3637 }
3638 
3639 } // namespace dhcp
3640 } // namespace isc
isc::dhcp::D2ClientMgr::qualifyName
std::string qualifyName(const std::string &partial_name, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
Definition: d2_client_mgr.cc:181
isc::dhcp::Pkt6
Represents a DHCPv6 packet.
Definition: pkt6.h:44
isc::dhcp::packet4_logger
isc::log::Logger packet4_logger(DHCP4_PACKET_LOGGER_NAME)
Logger for processed packets.
Definition: dhcp4_log.h:103
isc::dhcp::Dhcpv4Srv::deferredUnpack
void deferredUnpack(Pkt4Ptr &query)
Perform deferred option unpacking.
Definition: dhcp4_srv.cc:3406
isc::dhcp::DHCPDECLINE
@ DHCPDECLINE
Definition: dhcp4.h:231
isc::dhcp::OptionContainerPtr
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:206
isc::dhcp::OptionBuffer
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
isc::hooks::ScopedCalloutHandleState
Wrapper class around callout handle which automatically resets handle's state.
Definition: callout_handle.h:456
isc::dhcp::OptionContainerPersistRange
std::pair< OptionContainerPersistIndex::const_iterator, OptionContainerPersistIndex::const_iterator > OptionContainerPersistRange
Pair of iterators to represent the range of options having the same persistency flag.
Definition: cfg_option.h:220
isc::dhcp::D2ClientMgr::startSender
void startSender(D2ClientErrorHandler error_handler, isc::asiolink::IOService &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
Definition: d2_client_mgr.cc:233
isc::dhcp::ClientClassDictionaryPtr
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
Definition: client_class_def.h:382
isc::dhcp::Dhcpv4Srv::evaluateClasses
static void evaluateClasses(const Pkt4Ptr &pkt, bool depend_on_known)
Evaluate classes.
Definition: dhcp4_srv.cc:3266
docsis3_option_defs.h
isc::dhcp::Memfile_LeaseMgr::getDBVersion
static std::string getDBVersion()
Local version of getDBVersion() class method.
Definition: memfile_lease_mgr.cc:687
isc::dhcp::Dhcpv4Srv::getHookIndexPkt4Receive
static int getHookIndexPkt4Receive()
Returns the index for "pkt4_receive" hook point.
Definition: dhcp4_srv.cc:3607
isc::dhcp::Dhcpv4Srv::getHookIndexLease4Decline
static int getHookIndexLease4Decline()
Returns the index for "lease4_decline" hook point.
Definition: dhcp4_srv.cc:3627
isc::Unexpected
A generic exception that is thrown when an unexpected error condition occurs.
Definition: exceptions/exceptions.h:153
isc::dhcp::IfaceMgr::send
bool send(const Pkt6Ptr &pkt)
Sends an IPv6 packet.
Definition: iface_mgr.cc:953
isc::log
Definition: buffer_appender_impl.cc:17
isc::dhcp::ExpressionPtr
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
isc::dhcp::ConstCfgOptionPtr
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:500
hooks_log.h
isc::dhcp::SubnetSelector::ciaddr_
asiolink::IOAddress ciaddr_
ciaddr from the client's message.
Definition: subnet_selector.h:27
isc::process::Daemon::handleSignal
virtual void handleSignal()
Invokes handler for the next received signal.
Definition: daemon.cc:60
isc::dhcp::SubnetSelector::giaddr_
asiolink::IOAddress giaddr_
giaddr from the client's message.
Definition: subnet_selector.h:29
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
isc::dhcp::OptionDefinitionPtr
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
Definition: option_definition.h:51
isc::dhcp::Dhcpv4Srv::VENDOR_CLASS_PREFIX
static const std::string VENDOR_CLASS_PREFIX
this is a prefix added to the content of vendor-class option
Definition: dhcp4_srv.h:613
pkt4.h
isc::dhcp::PgSqlLeaseMgr::getDBVersion
static std::string getDBVersion()
Local version of getDBVersion() class method.
Definition: pgsql_lease_mgr.cc:1117
isc::dhcp::OptionDataTypeUtil::getLabelCount
static unsigned int getLabelCount(const std::string &text_name)
Return the number of labels in the Name.
Definition: option_data_types.cc:350
isc::dhcp::CfgMgr::instance
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
isc::dhcp::Host::IdentifierType
IdentifierType
Type of the host identifier.
Definition: host.h:252
isc::dhcp::Dhcpv4Srv::FORBIDDEN
@ FORBIDDEN
Definition: dhcp4_srv.h:204
isc::dhcp::SocketInfo::addr_
isc::asiolink::IOAddress addr_
Definition: socket_info.h:21
iface_mgr.h
isc::dhcp::Dhcpv4Srv::processPacket
void processPacket(Pkt4Ptr &query, Pkt4Ptr &rsp, bool allow_packet_park=true)
Process a single incoming DHCPv4 packet.
Definition: dhcp4_srv.cc:844
isc::dhcp::isClientClassBuiltIn
bool isClientClassBuiltIn(const ClientClass &client_class)
Check if a client class name is builtin.
Definition: client_class_def.cc:353
isc::dhcp::SubnetSelector::remote_address_
asiolink::IOAddress remote_address_
Source address of the message.
Definition: subnet_selector.h:45
isc::dhcp::Dhcpv4Srv::processRelease
void processRelease(Pkt4Ptr &release, AllocEngine::ClientContext4Ptr &context)
Processes incoming DHCPRELEASE messages.
Definition: dhcp4_srv.cc:2669
isc::dhcp::Dhcpv4Srv::getHookIndexPkt4Send
static int getHookIndexPkt4Send()
Returns the index for "pkt4_send" hook point.
Definition: dhcp4_srv.cc:3619
isc::dhcp::DHO_FQDN
@ DHO_FQDN
Definition: dhcp4.h:150
isc::dhcp::Option4ClientFqdnPtr
boost::shared_ptr< Option4ClientFqdn > Option4ClientFqdnPtr
A pointer to the Option4ClientFqdn object.
Definition: option4_client_fqdn.h:371
isc::dhcp::OptionCustom
Option with defined data fields represented as buffers that can be accessed using data field index.
Definition: option_custom.h:31
isc::dhcp::OptionString
Class which represents an option carrying a single string value.
Definition: option_string.h:27
isc::dhcp::OptionStringPtr
boost::shared_ptr< OptionString > OptionStringPtr
Pointer to the OptionString object.
Definition: option_string.h:117
isc::dhcp::Dhcpv4Srv::processDiscover
Pkt4Ptr processDiscover(Pkt4Ptr &discover)
Processes incoming DISCOVER and returns response.
Definition: dhcp4_srv.cc:2548
pgsql_lease_mgr.h
isc::dhcp::Dhcp4o6IpcBase::close
void close()
Close communication socket.
Definition: dhcp4o6_ipc.cc:118
isc::dhcp::Lease4Ptr
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:245
isc::dhcp::LeaseMgr::getLease4
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const =0
Returns an IPv4 lease for specified IPv4 address.
libdhcp++.h
isc::dhcp_ddns::NameChangeSender::Result
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:467
duid.h
isc::dhcp::Dhcpv4Srv::getVersion
static std::string getVersion(bool extended)
returns Kea version on stdout and exit.
Definition: dhcp4_srv.cc:3501
isc::dhcp::D2ClientMgr::generateFqdn
std::string generateFqdn(const asiolink::IOAddress &address, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
Definition: d2_client_mgr.cc:168
isc::hooks::CalloutHandlePtr
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
Definition: callout_handle.h:416
isc::hooks::hooks_logger
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition: hooks_log.h:37
isc::dhcp::DHCPDISCOVER
@ DHCPDISCOVER
Definition: dhcp4.h:228
isc::dhcp::Dhcpv4Srv::selectSubnet
isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr &query, bool &drop, bool sanity_only=false) const
Selects a subnet for a given client's packet.
Definition: dhcp4_srv.cc:517
config_report.h
isc::dhcp::Dhcpv4Srv::sanityCheck
static void sanityCheck(const Pkt4Ptr &query, RequirementLevel serverid)
Verifies if specified packet meets RFC requirements.
Definition: dhcp4_srv.cc:3202
isc::dhcp::Dhcpv4Srv::appendServerID
static void appendServerID(Dhcpv4Exchange &ex)
Adds server identifier option to the server's response.
Definition: dhcp4_srv.cc:1283
isc::dhcp::options4_logger
isc::log::Logger options4_logger(DHCP4_OPTIONS_LOGGER_NAME)
Logger for options parser.
Definition: dhcp4_log.h:109
isc::dhcp::DBG_DHCP4_START
const int DBG_DHCP4_START
Debug level used to log information during server startup.
Definition: dhcp4_log.h:24
D6O_INTERFACE_ID
@ D6O_INTERFACE_ID
Definition: dhcp6.h:38
isc::dhcp::AllocEngine::ALLOC_ITERATIVE
@ ALLOC_ITERATIVE
Definition: alloc_engine.h:230
isc::dhcp::Dhcpv4Srv::processInform
Pkt4Ptr processInform(Pkt4Ptr &inform)
Processes incoming DHCPINFORM messages.
Definition: dhcp4_srv.cc:2934
isc::util::str::trim
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
isc::dhcp::Dhcpv4Srv::selectSubnet4o6
isc::dhcp::Subnet4Ptr selectSubnet4o6(const Pkt4Ptr &query, bool &drop, bool sanity_only=false) const
Selects a subnet for a given client's DHCP4o6 packet.
Definition: dhcp4_srv.cc:603
isc::dhcp::AllocEngine
DHCPv4 and DHCPv6 allocation engine.
Definition: alloc_engine.h:56
isc::dhcp::DHO_SUBNET_MASK
@ DHO_SUBNET_MASK
Definition: dhcp4.h:70
isc::dhcp::Dhcpv4Exchange::getResponse
Pkt4Ptr getResponse() const
Returns the pointer to the server's response.
Definition: dhcp4_srv.h:101
isc::dhcp::Dhcpv4Srv::receivePacket
virtual Pkt4Ptr receivePacket(int timeout)
dummy wrapper around IfaceMgr::receive4
Definition: dhcp4_srv.cc:721
isc::dhcp::Option4ClientFqdn::getDomainName
std::string getDomainName() const
Returns the domain-name in the text format.
Definition: option4_client_fqdn.cc:432
hooks_manager.h
isc::dhcp::DHO_DOMAIN_NAME_SERVERS
@ DHO_DOMAIN_NAME_SERVERS
Definition: dhcp4.h:75
isc::dhcp::ClientClasses::toText
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition: classify.cc:34
isc::dhcp_ddns::NameChangeRequestPtr
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:212
pkt6.h
isc::dhcp::Option4ClientFqdn::getFlag
bool getFlag(const uint8_t flag) const
Checks if the specified flag of the DHCPv4 Client FQDN Option is set.
Definition: option4_client_fqdn.cc:377
cfg_host_operations.h
isc::dhcp::ScopedEnableOptionsCopy
RAII object enabling copying options retrieved from the packet.
Definition: pkt.h:40
isc::dhcp::Dhcpv4Srv::getIOService
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition: dhcp4_srv.h:234
ncr_generator.h
isc::dhcp::CqlLeaseMgr::getDBVersion
static std::string getDBVersion()
Local version of getDBVersion() class method.
Definition: cql_lease_mgr.cc:2022
isc::dhcp::Dhcpv4Exchange::deleteResponse
void deleteResponse()
Removes the response message by resetting the pointer to NULL.
Definition: dhcp4_srv.h:106
isc::dhcp::Dhcpv4Srv::appendRequestedOptions
void appendRequestedOptions(Dhcpv4Exchange &ex)
Appends options requested by client.
Definition: dhcp4_srv.cc:1381
isc::dhcp::D2ClientConfig::RCM_WHEN_PRESENT
@ RCM_WHEN_PRESENT
Definition: d2_client_cfg.h:75
lease_mgr_factory.h
isc::dhcp::SubnetSelector
Subnet selector used to specify parameters used to select a subnet.
Definition: subnet_selector.h:23
option_string.h
cfg_subnets4.h
isc::dhcp::LibDHCP::getRuntimeOptionDef
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:205
isc::dhcp::Pkt4o6Ptr
boost::shared_ptr< Pkt4o6 > Pkt4o6Ptr
A pointer to Pkt4o6 object.
Definition: pkt4o6.h:82
isc::dhcp::D2ClientMgr::suspendUpdates
void suspendUpdates()
Suspends sending requests.
Definition: d2_client_mgr.cc:34
isc::dhcp::Dhcpv4Exchange::setReservedMessageFields
void setReservedMessageFields()
Sets reserved values of siaddr, sname and file in the server's response.
Definition: dhcp4_srv.cc:420
isc::dhcp::CfgSharedNetworks4Ptr
boost::shared_ptr< CfgSharedNetworks4 > CfgSharedNetworks4Ptr
Pointer to the configuration of IPv4 shared networks.
Definition: cfg_shared_networks.h:131
isc::dhcp::Dhcpv4Srv::getHookIndexLease4Release
static int getHookIndexLease4Release()
Returns the index for "lease4_release" hook point.
Definition: dhcp4_srv.cc:3615
isc::dhcp::Dhcpv4Srv::acceptDirectRequest
bool acceptDirectRequest(const Pkt4Ptr &query) const
Check if a message sent by directly connected client should be accepted or discarded.
Definition: dhcp4_srv.cc:3011
isc::dhcp::Dhcpv4Srv::sendPacket
virtual void sendPacket(const Pkt4Ptr &pkt)
dummy wrapper around IfaceMgr::send()
Definition: dhcp4_srv.cc:726
isc::dhcp::DHO_DHCP_AGENT_OPTIONS
@ DHO_DHCP_AGENT_OPTIONS
Definition: dhcp4.h:151
isc::dhcp::Dhcpv4Srv::alloc_engine_
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition: dhcp4_srv.h:911
isc::dhcp::Dhcpv4Exchange::initResponse4o6
void initResponse4o6()
Initializes the DHCPv6 part of the response message.
Definition: dhcp4_srv.cc:219
isc::dhcp::Dhcpv4Srv::processDecline
void processDecline(Pkt4Ptr &decline, AllocEngine::ClientContext4Ptr &context)
Process incoming DHCPDECLINE messages.
Definition: dhcp4_srv.cc:2784
isc::dhcp::CfgSubnets4::initSelector
static SubnetSelector initSelector(const Pkt4Ptr &query)
Build selector from a client's message.
Definition: cfg_subnets4.cc:80
isc::dhcp::Dhcpv4Srv::Dhcpv4Srv
Dhcpv4Srv(uint16_t port=DHCP4_SERVER_PORT, const bool use_bcast=true, const bool direct_response_desired=true)
Default constructor.
Definition: dhcp4_srv.cc:444
isc::dhcp::Option4ClientFqdn::FULL
@ FULL
Definition: option4_client_fqdn.h:150
isc::dhcp_ddns::CHG_REMOVE
@ CHG_REMOVE
Definition: ncr_msg.h:48
Hooks
Dhcp4Hooks Hooks
Definition: dhcp4_srv.cc:117
isc::dhcp::DHCP_TYPES_EOF
@ DHCP_TYPES_EOF
Definition: dhcp4.h:246
isc::dhcp::Dhcpv4Srv::processPacketBufferSend
void processPacketBufferSend(hooks::CalloutHandlePtr &callout_handle, Pkt4Ptr &rsp)
Executes buffer4_send callout and sends the response.
Definition: dhcp4_srv.cc:1190
isc::dhcp::D2ClientMgr::ddnsEnabled
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
Definition: d2_client_mgr.cc:106
lease_mgr.h
An abstract API for lease database.
isc::dhcp::LeaseMgrFactory::instance
static LeaseMgr & instance()
Return current lease manager.
Definition: lease_mgr_factory.cc:111
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition: exceptions/exceptions.h:23
isc::dhcp::SharedNetwork4Ptr
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
Definition: shared_network.h:163
shared_network.h
isc::dhcp::Dhcpv4Srv::getHookIndexSubnet4Select
static int getHookIndexSubnet4Select()
Returns the index for "subnet4_select" hook point.
Definition: dhcp4_srv.cc:3611
isc::dhcp::DBG_DHCP4_DETAIL_DATA
const int DBG_DHCP4_DETAIL_DATA
This level is used to log the contents of packets received and sent.
Definition: dhcp4_log.h:56
dhcp4.h
isc::dhcp::LeaseMgrFactory::destroy
static void destroy()
Destroy lease manager.
Definition: lease_mgr_factory.cc:95
logger.h
isc::dhcp::Dhcpv4Srv::~Dhcpv4Srv
virtual ~Dhcpv4Srv()
Destructor. Used during DHCPv4 service shutdown.
Definition: dhcp4_srv.cc:482
isc::dhcp::Dhcpv4Srv::appendBasicOptions
void appendBasicOptions(Dhcpv4Exchange &ex)
Append basic options if they are not present.
Definition: dhcp4_srv.cc:1540
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
dhcp4_srv.h
isc::process::Daemon::signal_set_
isc::util::SignalSetPtr signal_set_
A pointer to the object installing custom signal handlers.
Definition: daemon.h:238
isc::dhcp::D2ClientConfig::ReplaceClientNameMode
ReplaceClientNameMode
Defines the client name replacement modes.
Definition: d2_client_cfg.h:72
isc::dhcp::SubnetSelector::first_relay_linkaddr_
asiolink::IOAddress first_relay_linkaddr_
First relay link address.
Definition: subnet_selector.h:39
isc::dhcp::HWAddrPtr
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition: exceptions/exceptions.cc:32
isc::dhcp::Pkt4Ptr
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
isc::dhcp::DHO_DHCP_REQUESTED_ADDRESS
@ DHO_DHCP_REQUESTED_ADDRESS
Definition: dhcp4.h:119
subnet_selector.h
isc::dhcp::DHO_DHCP_SERVER_IDENTIFIER
@ DHO_DHCP_SERVER_IDENTIFIER
Definition: dhcp4.h:123
strutil.h
isc::dhcp::Dhcpv4Exchange::getCfgOptionList
CfgOptionList & getCfgOptionList()
Returns the configured option list (non-const version)
Definition: dhcp4_srv.h:116
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
cfg_iface.h
utils.h
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
isc::dhcp::DHO_DHCP_LEASE_TIME
@ DHO_DHCP_LEASE_TIME
Definition: dhcp4.h:120
isc::dhcp::ClientClasses::cbegin
const_iterator cbegin() const
Iterator to the first element.
Definition: classify.h:81
isc::dhcp::Dhcpv4Srv::shutdown
void shutdown()
Instructs the server to shut down.
Definition: dhcp4_srv.cc:511
isc::dhcp::Host::IDENT_CIRCUIT_ID
@ IDENT_CIRCUIT_ID
Definition: host.h:255
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::dhcp::Pkt6::RELAY_GET_FIRST
@ RELAY_GET_FIRST
Definition: pkt6.h:77
isc::dhcp::CfgIface::USE_ROUTING
@ USE_ROUTING
Server uses routing to determine the right interface to send response.
Definition: cfg_iface.h:147
isc::dhcp::Option4ClientFqdn
Represents DHCPv4 Client FQDN Option (code 81).
Definition: option4_client_fqdn.h:110
isc::dhcp::DBG_DHCP4_BASIC
const int DBG_DHCP4_BASIC
Debug level used to trace basic operations within the code.
Definition: dhcp4_log.h:33
isc::dhcp::IfaceMgr::setMatchingPacketFilter
void setMatchingPacketFilter(const bool direct_response_desired=false)
Set Packet Filter object to handle send/receive packets.
isc::dhcp::D2ClientMgr::adjustFqdnFlags
void adjustFqdnFlags(const T &fqdn, T &fqdn_resp)
Set server FQDN flags based on configuration and a given FQDN.
Definition: d2_client_mgr.h:438
isc::dhcp::Dhcpv4Srv::processStatsSent
static void processStatsSent(const Pkt4Ptr &response)
Updates statistics for transmitted packets.
Definition: dhcp4_srv.cc:3577
evaluate.h
isc::dhcp::Dhcpv4Srv::run
bool run()
Main server processing loop.
Definition: dhcp4_srv.cc:731
isc::dhcp::D2ClientMgr::adjustDomainName
void adjustDomainName(const T &fqdn, T &fqdn_resp)
Set server FQDN name based on configuration and a given FQDN.
Definition: d2_client_mgr.h:465
isc::dhcp::Host::IDENT_CLIENT_ID
@ IDENT_CLIENT_ID
Definition: host.h:256
isc::dhcp::D2ClientConfig::RCM_ALWAYS
@ RCM_ALWAYS
Definition: d2_client_cfg.h:74
isc::dhcp::AllocEngine::ClientContext4Ptr
boost::shared_ptr< ClientContext4 > ClientContext4Ptr
Pointer to the ClientContext4.
Definition: alloc_engine.h:1246
isc::dhcp::OptionVendor::getVendorId
uint32_t getVendorId() const
Returns enterprise identifier.
Definition: option_vendor.h:84
isc::dhcp::DBG_DHCP4_HOOKS
const int DBG_DHCP4_HOOKS
Debug level used to trace hook related operations.
Definition: dhcp4_log.h:36
isc::dhcp::ClientId
Holds Client identifier or client IPv4 address.
Definition: duid.h:111
isc::dhcp::Option4ClientFqdn::setFlag
void setFlag(const uint8_t flag, const bool set)
Modifies the value of the specified DHCPv4 Client Fqdn Option flag.
Definition: option4_client_fqdn.cc:389
isc::dhcp::Dhcpv4Srv::processPacketPktSend
void processPacketPktSend(hooks::CalloutHandlePtr &callout_handle, Pkt4Ptr &query, Pkt4Ptr &rsp)
Executes pkt4_send callout.
Definition: dhcp4_srv.cc:1132
isc::dhcp::ddns4_logger
isc::log::Logger ddns4_logger(DHCP4_DDNS_LOGGER_NAME)
Logger for Hostname or FQDN processing.
Definition: dhcp4_log.h:115
isc::dhcp::DHO_ROUTERS
@ DHO_ROUTERS
Definition: dhcp4.h:72
isc::dhcp::ClientClassDefPtr
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
Definition: client_class_def.h:252
isc::dhcp::DHCPNAK
@ DHCPNAK
Definition: dhcp4.h:233
isc::dhcp::SubnetSelector::interface_id_
OptionPtr interface_id_
Interface id option.
Definition: subnet_selector.h:37
isc::dhcp::IfaceMgr::closeSockets
void closeSockets()
Closes all open sockets.
Definition: iface_mgr.cc:282
isc::dhcp::Dhcpv4Srv::MANDATORY
@ MANDATORY
Definition: dhcp4_srv.h:205
isc::dhcp::evaluateBool
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
isc::dhcp::IfaceMgr::isDirectResponseSupported
bool isDirectResponseSupported() const
Check if packet be sent directly to the client having no address.
Definition: iface_mgr.cc:312
isc::dhcp::IfaceMgr::instance
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
isc::dhcp::DHO_VENDOR_CLASS_IDENTIFIER
@ DHO_VENDOR_CLASS_IDENTIFIER
Definition: dhcp4.h:129
isc::dhcp::bad_packet4_logger
isc::log::Logger bad_packet4_logger(DHCP4_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition: dhcp4_log.h:97
isc::dhcp
Definition: ctrl_dhcp4_srv.cc:75
isc::dhcp::LibDHCP::getOptionDef
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition: libdhcp++.cc:144
dhcp4_log.h
isc::util::str::StringSanitizerPtr
boost::shared_ptr< StringSanitizer > StringSanitizerPtr
Definition: strutil.h:305
subnet.h
DHCP4_OPTION_SPACE
#define DHCP4_OPTION_SPACE
Definition: option_space.h:16
DHCPV6_DHCPV4_RESPONSE
@ DHCPV6_DHCPV4_RESPONSE
Definition: dhcp6.h:237
isc::dhcp_ddns
Definition: dhcp_ddns_log.cc:14
isc::dhcp::NetworkState
Holds information about DHCP service enabling status.
Definition: network_state.h:57
isc::dhcp::Subnet4Ptr
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:464
addr_utilities.h
isc::dhcp::Dhcpv4Srv::d2ClientErrorHandler
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:3486
memfile_lease_mgr.h
isc::dhcp::Dhcpv4Srv::getHookIndexBuffer4Receive
static int getHookIndexBuffer4Receive()
Returns the index for "buffer4_receive" hook point.
Definition: dhcp4_srv.cc:3603
isc::dhcp::PoolPtr
boost::shared_ptr< Pool > PoolPtr
a pointer to either IPv4 or IPv6 Pool
Definition: pool.h:405
isc::dhcp::ConstCfgHostOperationsPtr
boost::shared_ptr< const CfgHostOperations > ConstCfgHostOperationsPtr
Pointer to the const object.
Definition: cfg_host_operations.h:29
isc::dhcp::SkipRemainingOptionsError
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition: option.h:52
isc::dhcp::OptionCustomPtr
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
Definition: option_custom.h:464
isc::dhcp::Dhcpv4Srv::checkRelayPort
static uint16_t checkRelayPort(const Dhcpv4Exchange &ex)
Check if the relay port RAI sub-option was set in the query.
Definition: dhcp4_srv.cc:2254
isc::stats
Definition: context.cc:13
isc::dhcp::Dhcpv4Srv::acceptServerId
bool acceptServerId(const Pkt4Ptr &pkt) const
Verifies if the server id belongs to our server.
Definition: dhcp4_srv.cc:3105
isc::dhcp::Dhcpv4Srv::buildCfgOptionList
void buildCfgOptionList(Dhcpv4Exchange &ex)
Build the configured option list.
Definition: dhcp4_srv.cc:1308
LOG_WARN
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
isc::dhcp::Lease::TYPE_V4
@ TYPE_V4
IPv4 lease.
Definition: lease.h:42
isc::dhcp::ClientClasses::empty
bool empty() const
Check if classes is empty.
Definition: classify.h:68
isc::dhcp::OptionUint32
OptionInt< uint32_t > OptionUint32
Definition: option_int.h:34
cfg_shared_networks.h
isc::dhcp::Pkt4
Represents DHCPv4 packet.
Definition: pkt4.h:38
isc::dhcp::Dhcpv4Exchange
DHCPv4 message exchange.
Definition: dhcp4_srv.h:63
isc::dhcp::Dhcpv4Exchange::initResponse
void initResponse()
Initializes the instance of the response message.
Definition: dhcp4_srv.cc:193
isc::dhcp::Pkt6::RelayInfo
structure that describes a single relay information
Definition: pkt6.h:85
isc::dhcp::lease4_logger
isc::log::Logger lease4_logger(DHCP4_LEASE_LOGGER_NAME)
Logger for lease allocation logic.
Definition: dhcp4_log.h:120
isc::dhcp::Pkt4::FLAG_BROADCAST_MASK
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:55
isc::dhcp::Lease4CollectionPtr
boost::shared_ptr< Lease4Collection > Lease4CollectionPtr
A shared pointer to the collection of IPv4 leases.
Definition: lease.h:458
stats_mgr.h
isc::dhcp::SignalInterruptOnSelect
Exception thrown when a call to select is interrupted by a signal.
Definition: iface_mgr.h:51
isc::dhcp::Dhcpv4Srv::classifyPacket
void classifyPacket(const Pkt4Ptr &pkt)
Assigns incoming packet to zero or more classes.
Definition: dhcp4_srv.cc:3255
isc::dhcp::Host::IDENT_HWADDR
@ IDENT_HWADDR
Definition: host.h:253
isc::dhcp_ddns::CHG_ADD
@ CHG_ADD
Definition: ncr_msg.h:47
isc::dhcp::Network::HR_DISABLED
@ HR_DISABLED
None - host reservation is disabled.
Definition: network.h:95
isc::dhcp::Dhcpv4Srv::adjustIfaceData
static void adjustIfaceData(Dhcpv4Exchange &ex)
Set IP/UDP and interface parameters for the DHCPv4 response.
Definition: dhcp4_srv.cc:2267
isc::dhcp::OptionDescriptor::option_
OptionPtr option_
Option instance.
Definition: cfg_option.h:38
isc::dhcp::SocketInfo
Holds information about socket.
Definition: socket_info.h:19
isc::dhcp::Dhcpv4Srv::acceptMessageType
bool acceptMessageType(const Pkt4Ptr &query) const
Check if received message type is valid for the server to process.
Definition: dhcp4_srv.cc:3049
isc::dhcp::LeaseMgr::updateLease4
virtual void updateLease4(const Lease4Ptr &lease4)=0
Updates IPv4 lease.
option_vendor.h
isc::dhcp::AllocEnginePtr
boost::shared_ptr< AllocEngine > AllocEnginePtr
A pointer to the AllocEngine object.
Definition: alloc_engine.h:1611
isc::dhcp::LeaseMgr::deleteLease
virtual bool deleteLease(const isc::asiolink::IOAddress &addr)=0
Deletes a lease.
hwaddr.h
isc::dhcp::Dhcpv4Exchange::getQuery
Pkt4Ptr getQuery() const
Returns the pointer to the query from the client.
Definition: dhcp4_srv.h:94
isc::dhcp::Host::getIdentifierAsText
std::string getIdentifierAsText() const
Returns host identifier in a textual form.
Definition: host.cc:239
isc::dhcp::DHO_HOST_NAME
@ DHO_HOST_NAME
Definition: dhcp4.h:81
isc::dhcp::Dhcpv4Srv::getHookIndexBuffer4Send
static int getHookIndexBuffer4Send()
Returns the index for "buffer4_send" hook point.
Definition: dhcp4_srv.cc:3623
isc::dhcp::RFCViolation
An exception that is thrown if a DHCPv6 protocol violation occurs while processing a message (e....
Definition: utils.h:17
isc::dhcp::OptionInt
Forward declaration to OptionInt.
Definition: option_definition.h:64
isc::dhcp::Dhcpv4Srv::processClientName
void processClientName(Dhcpv4Exchange &ex)
Processes Client FQDN and Hostname Options sent by a client.
Definition: dhcp4_srv.cc:1586
isc::dhcp::DBG_DHCP4_DETAIL
const int DBG_DHCP4_DETAIL
Debug level used to trace detailed errors.
Definition: dhcp4_log.h:53
isc::log::DBGLVL_TRACE_BASIC
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:65
isc::dhcp::ClientIdPtr
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition: duid.h:103
isc::detail::config_report
const char *const config_report[]
isc::dhcp::DHCPOFFER
@ DHCPOFFER
Definition: dhcp4.h:229
isc::dhcp::Dhcpv4Srv::assignLease
void assignLease(Dhcpv4Exchange &ex)
Assigns a lease and appends corresponding options.
Definition: dhcp4_srv.cc:1864
isc::dhcp::D2ClientConfig::RCM_WHEN_NOT_PRESENT
@ RCM_WHEN_NOT_PRESENT
Definition: d2_client_cfg.h:76
isc::dhcp::D2ClientMgr
D2ClientMgr isolates Kea from the details of being a D2 client.
Definition: d2_client_mgr.h:79
DOCSIS3_V4_ORO
#define DOCSIS3_V4_ORO
Definition: docsis3_option_defs.h:18
isc::dhcp::Dhcpv4Exchange::setReservedClientClasses
void setReservedClientClasses()
Assigns classes retrieved from host reservation database.
Definition: dhcp4_srv.cc:409
isc::dhcp::DHCPINFORM
@ DHCPINFORM
Definition: dhcp4.h:235
cfgmgr.h
isc::dhcp::D2ClientMgr::getUpdateDirections
void getUpdateDirections(const T &fqdn_resp, bool &forward, bool &reverse)
Get directional update flags based on server FQDN flags.
Definition: d2_client_mgr.h:457
mysql_lease_mgr.h
isc::dhcp::CfgIface::SOCKET_UDP
@ SOCKET_UDP
Datagram socket, i.e. IP/UDP socket.
Definition: cfg_iface.h:138
isc::dhcp::Option4AddrLst::AddressContainer
std::vector< isc::asiolink::IOAddress > AddressContainer
Defines a collection of IPv4 addresses.
Definition: option4_addrlst.h:38
isc::dhcp::LibDHCP::getLastResortOptionDef
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition: libdhcp++.cc:265
isc::dhcp::ClientClasses::const_iterator
std::list< ClientClass >::const_iterator const_iterator
Type of iterators.
Definition: classify.h:47
isc::dhcp::Dhcpv4Srv::OPTIONAL
@ OPTIONAL
Definition: dhcp4_srv.h:206
isc::dhcp::Dhcpv4Srv::run_one
void run_one()
Main server processing step.
Definition: dhcp4_srv.cc:753
isc::dhcp::CfgMgr::getCurrentCfg
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:154
isc::dhcp::Dhcpv4Srv::processStatsReceived
static void processStatsReceived(const Pkt4Ptr &query)
Class methods for DHCPv4-over-DHCPv6 handler.
Definition: dhcp4_srv.cc:3528
isc::dhcp::Dhcpv4Srv::stopD2
void stopD2()
Stops DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp4_srv.cc:3477
isc::dhcp::OptionVendor
This class represents vendor-specific information option.
Definition: option_vendor.h:30
isc::dhcp::Dhcpv4Srv::declineLease
void declineLease(const Lease4Ptr &lease, const Pkt4Ptr &decline, AllocEngine::ClientContext4Ptr &context)
Marks lease as declined.
Definition: dhcp4_srv.cc:2858
isc::dhcp::CfgMgr::getD2ClientMgr
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition: cfgmgr.cc:65
isc::dhcp::Dhcpv4Srv::network_state_
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition: dhcp4_srv.h:937
dhcp4to6_ipc.h
isc::dhcp::Lease4Collection
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition: lease.h:455
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
isc::dhcp::DHO_DOMAIN_NAME
@ DHO_DOMAIN_NAME
Definition: dhcp4.h:84
isc::dhcp::SubnetSelector::client_classes_
ClientClasses client_classes_
Classes that the client belongs to.
Definition: subnet_selector.h:47
isc::dhcp::ConstHostPtr
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:728
isc::dhcp::Dhcpv4Srv::classifyByVendor
void classifyByVendor(const Pkt4Ptr &pkt)
Assign class using vendor-class-identifier option.
Definition: dhcp4_srv.cc:3243
isc::dhcp::OptionDescriptor
Option descriptor.
Definition: cfg_option.h:35
pkt4o6.h
isc::dhcp::SubnetSelector::iface_name_
std::string iface_name_
Name of the interface on which the message was received.
Definition: subnet_selector.h:49
isc::dhcp::DHO_VIVSO_SUBOPTIONS
@ DHO_VIVSO_SUBOPTIONS
Definition: dhcp4.h:185
isc::dhcp::OptionContainerPersistIndex
OptionContainer::nth_index< 2 >::type OptionContainerPersistIndex
Type of the index #2 - option persistency flag.
Definition: cfg_option.h:215
isc::dhcp::ClientClasses::insert
void insert(const ClientClass &class_name)
Insert an element.
Definition: classify.h:62
isc::dhcp::Dhcp4to6Ipc::instance
static Dhcp4to6Ipc & instance()
Returns pointer to the sole instance of Dhcp4to6Ipc.
Definition: dhcp4to6_ipc.cc:32
isc::hooks
Definition: callout_handle.cc:21
isc::dhcp::Dhcpv4Exchange::getContext
AllocEngine::ClientContext4Ptr getContext() const
Returns the copy of the context for the Allocation engine.
Definition: dhcp4_srv.h:111
isc::dhcp::Dhcpv4Srv::createNameChangeRequests
void createNameChangeRequests(const Lease4Ptr &lease, const Lease4Ptr &old_lease)
Creates NameChangeRequests which correspond to the lease which has been acquired.
Definition: dhcp4_srv.cc:1844
isc::dhcp::OptionIntArray
Forward declaration to OptionIntArray.
Definition: option_definition.h:74
isc::dhcp::Dhcpv4Srv::shutdown_
volatile bool shutdown_
indicates if shutdown is in progress.
Definition: dhcp4_srv.h:827
isc::dhcp::SubnetSelector::local_address_
asiolink::IOAddress local_address_
Address on which the message was received.
Definition: subnet_selector.h:43
isc::dhcp::OptionUint32Ptr
boost::shared_ptr< OptionUint32 > OptionUint32Ptr
Definition: option_int.h:35
isc::dhcp::Dhcpv4Srv::srvidToString
static std::string srvidToString(const OptionPtr &opt)
converts server-id to text Converts content of server-id option to a text representation,...
Definition: dhcp4_srv.cc:1263
isc::dhcp::IfaceMgr::getSocket
uint16_t getSocket(const isc::dhcp::Pkt6 &pkt)
Return most suitable socket for transmitting specified IPv6 packet.
Definition: iface_mgr.cc:1622
callout_handle.h
isc::dhcp::DHO_DHCP_RENEWAL_TIME
@ DHO_DHCP_RENEWAL_TIME
Definition: dhcp4.h:127
isc::dhcp::Dhcpv4Srv::accept
bool accept(const Pkt4Ptr &query) const
Checks whether received message should be processed or discarded.
Definition: dhcp4_srv.cc:2983
isc::dhcp::DHCPACK
@ DHCPACK
Definition: dhcp4.h:232
isc::dhcp::getNetmask4
isc::asiolink::IOAddress getNetmask4(uint8_t len)
Generates an IPv4 netmask of specified length.
Definition: addr_utilities.cc:198
isc::dhcp::dhcp4_logger
isc::log::Logger dhcp4_logger(DHCP4_APP_LOGGER_NAME)
Base logger for DHCPv4 server.
Definition: dhcp4_log.h:90
isc::dhcp::Option4ClientFqdn::FLAG_E
static const uint8_t FLAG_E
Bit E.
Definition: option4_client_fqdn.h:118
isc::dhcp::MySqlLeaseMgr::getDBVersion
static std::string getDBVersion()
Local version of getDBVersion() class method.
Definition: mysql_lease_mgr.cc:1710
isc::dhcp::Dhcpv4Srv::RequirementLevel
RequirementLevel
defines if certain option may, must or must not appear
Definition: dhcp4_srv.h:203
isc::dhcp::dhcp4_config_report
const char *const * dhcp4_config_report
Definition: dhcp4_srv.cc:3498
isc::dhcp::DBG_DHCP4_BASIC_DATA
const int DBG_DHCP4_BASIC_DATA
Debug level used to log the traces with some basic data.
Definition: dhcp4_log.h:45
isc::dhcp::D2ClientMgr::stopSender
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
Definition: d2_client_mgr.cc:268
cql_lease_mgr.h
isc::dhcp::DHO_DHCP_CLIENT_IDENTIFIER
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition: dhcp4.h:130
isc::stats::StatsMgr::instance
static StatsMgr & instance()
Statistics Manager accessor method.
Definition: stats_mgr.cc:21
isc::dhcp::DHO_DHCP_REBINDING_TIME
@ DHO_DHCP_REBINDING_TIME
Definition: dhcp4.h:128
isc::dhcp::Dhcpv4Srv::processRequest
Pkt4Ptr processRequest(Pkt4Ptr &request, AllocEngine::ClientContext4Ptr &context)
Processes incoming REQUEST and returns REPLY response.
Definition: dhcp4_srv.cc:2607
isc::dhcp::SubnetSelector::option_select_
asiolink::IOAddress option_select_
RAI link select or subnet select option.
Definition: subnet_selector.h:31
isc::dhcp::D2ClientMgr::getD2ClientConfig
const D2ClientConfigPtr & getD2ClientConfig() const
Fetches the DHCP-DDNS configuration pointer.
Definition: d2_client_mgr.cc:111
isc::dhcp::Dhcpv4Srv::setFixedFields
void setFixedFields(Dhcpv4Exchange &ex)
Sets fixed fields of the outgoing packet.
Definition: dhcp4_srv.cc:2445
isc::stats::StatsMgr::addValue
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
Definition: stats_mgr.cc:46
isc::dhcp::Pkt4o6
Represents DHCPv4-over-DHCPv6 packet.
Definition: pkt4o6.h:30
isc::dhcp::DHCP_NOTYPE
@ DHCP_NOTYPE
Message Type option missing.
Definition: dhcp4.h:227
isc::dhcp::Dhcpv4Srv::discardPackets
void discardPackets()
Discard all in-progress packets.
Definition: dhcp4_srv.cc:3631
isc::dhcp::DHO_DHCP_PARAMETER_REQUEST_LIST
@ DHO_DHCP_PARAMETER_REQUEST_LIST
Definition: dhcp4.h:124
isc::dhcp::ConstCfgSubnets4Ptr
boost::shared_ptr< const CfgSubnets4 > ConstCfgSubnets4Ptr
Const pointer.
Definition: cfg_subnets4.h:273
isc::dhcp::SrvConfigPtr
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
Definition: srv_config.h:707
isc::dhcp::SubnetSelector::dhcp4o6_
bool dhcp4o6_
Specifies if the packet is DHCP4o6.
Definition: subnet_selector.h:52
isc::dhcp::Pkt6::RelayInfo::linkaddr_
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
isc::dhcp::Dhcpv4Srv::startD2
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp4_srv.cc:3465
option_custom.h
isc::dhcp::Host::IDENT_FLEX
@ IDENT_FLEX
Flexible host identifier.
Definition: host.h:257
option_int.h
option4_addrlst.h
option_int_array.h
isc::dhcp::Dhcpv4Srv::adjustRemoteAddr
static void adjustRemoteAddr(Dhcpv4Exchange &ex)
Sets remote addresses for outgoing packet.
Definition: dhcp4_srv.cc:2348
isc::dhcp::CfgMgr
Configuration Manager.
Definition: cfgmgr.h:69
isc::dhcp::CfgIfacePtr
boost::shared_ptr< CfgIface > CfgIfacePtr
A pointer to the CfgIface .
Definition: cfg_iface.h:387
isc::dhcp::DHO_SUBNET_SELECTION
@ DHO_SUBNET_SELECTION
Definition: dhcp4.h:178
isc::dhcp::ClientClasses::cend
const_iterator cend() const
Iterator to the past the end element.
Definition: classify.h:86
isc::dhcp::Option::V4
@ V4
Definition: option.h:67
isc::dhcp::queueNCR
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
Definition: ncr_generator.cc:86
isc::dhcp::DHCPRELEASE
@ DHCPRELEASE
Definition: dhcp4.h:234
isc::dhcp::Pkt6Ptr
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
isc::dhcp::DHCPREQUEST
@ DHCPREQUEST
Definition: dhcp4.h:230
isc::dhcp::ClientClassDefListPtr
boost::shared_ptr< ClientClassDefList > ClientClassDefListPtr
Defines a pointer to a ClientClassDefList.
Definition: client_class_def.h:264
isc::dhcp::OptionUint8ArrayPtr
boost::shared_ptr< OptionUint8Array > OptionUint8ArrayPtr
Definition: option_int_array.h:31
isc::dhcp::Host::IDENT_DUID
@ IDENT_DUID
Definition: host.h:254
isc::dhcp::ClientClasses
Container for storing client class names.
Definition: classify.h:43
isc::dhcp::AllocEngine::ClientContext4
Context information for the DHCPv4 lease allocation.
Definition: alloc_engine.h:1137
isc::dhcp::CfgOptionList
std::list< ConstCfgOptionPtr > CfgOptionList
Const pointer list.
Definition: cfg_option.h:503
isc::dhcp::Option4AddrLst
DHCPv4 Option class for handling list of IPv4 addresses.
Definition: option4_addrlst.h:34
isc::dhcp::Dhcpv4Srv::requiredClassify
void requiredClassify(Dhcpv4Exchange &ex)
Assigns incoming packet to zero or more classes (required pass).
Definition: dhcp4_srv.cc:3314
isc::dhcp::Dhcpv4Srv::appendRequestedVendorOptions
void appendRequestedVendorOptions(Dhcpv4Exchange &ex)
Appends requested vendor options as requested by client.
Definition: dhcp4_srv.cc:1452
isc::dhcp::getCalloutHandle
isc::hooks::CalloutHandlePtr getCalloutHandle(const T &pktptr)
CalloutHandle Store.
Definition: callout_handle_store.h:47