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