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