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