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