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