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