Kea 1.5.0
dhcp4_srv.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2018 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>
25#include <dhcp4/dhcp4to6_ipc.h>
26#include <dhcp4/dhcp4_log.h>
27#include <dhcp4/dhcp4_srv.h>
29#include <dhcpsrv/cfgmgr.h>
31#include <dhcpsrv/cfg_iface.h>
34#include <dhcpsrv/lease_mgr.h>
38#include <dhcpsrv/subnet.h>
40#include <dhcpsrv/utils.h>
41#include <dhcpsrv/utils.h>
42#include <eval/evaluate.h>
43#include <eval/eval_messages.h>
45#include <hooks/hooks_log.h>
46#include <hooks/hooks_manager.h>
47#include <stats/stats_mgr.h>
48#include <util/strutil.h>
49#include <stats/stats_mgr.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/bind.hpp>
67#include <boost/foreach.hpp>
68#include <boost/pointer_cast.hpp>
69#include <boost/shared_ptr.hpp>
70
71#include <iomanip>
72
73using namespace isc;
74using namespace isc::asiolink;
75using namespace isc::cryptolink;
76using namespace isc::dhcp;
77using namespace isc::dhcp_ddns;
78using namespace isc::hooks;
79using namespace isc::log;
80using namespace isc::stats;
81using namespace std;
82
83namespace {
84
86struct Dhcp4Hooks {
87 int hook_index_buffer4_receive_;
88 int hook_index_pkt4_receive_;
89 int hook_index_subnet4_select_;
90 int hook_index_leases4_committed_;
91 int hook_index_lease4_release_;
92 int hook_index_pkt4_send_;
93 int hook_index_buffer4_send_;
94 int hook_index_lease4_decline_;
95 int hook_index_host4_identifier_;
96
98 Dhcp4Hooks() {
99 hook_index_buffer4_receive_ = HooksManager::registerHook("buffer4_receive");
100 hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive");
101 hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
102 hook_index_leases4_committed_ = HooksManager::registerHook("leases4_committed");
103 hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send");
104 hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
105 hook_index_buffer4_send_ = HooksManager::registerHook("buffer4_send");
106 hook_index_lease4_decline_ = HooksManager::registerHook("lease4_decline");
107 hook_index_host4_identifier_ = HooksManager::registerHook("host4_identifier");
108 }
109};
110
111} // end of anonymous namespace
112
113// Declare a Hooks object. As this is outside any function or method, it
114// will be instantiated (and the constructor run) when the module is loaded.
115// As a result, the hook indexes will be defined before any method in this
116// module is called.
117Dhcp4Hooks Hooks;
118
119
120namespace isc {
121namespace dhcp {
122
124 const Pkt4Ptr& query,
125 const Subnet4Ptr& subnet)
126 : alloc_engine_(alloc_engine), query_(query), resp_(),
127 context_(new AllocEngine::ClientContext4()) {
128
129 if (!alloc_engine_) {
130 isc_throw(BadValue, "alloc_engine value must not be NULL"
131 " when creating an instance of the Dhcpv4Exchange");
132 }
133
134 if (!query_) {
135 isc_throw(BadValue, "query value must not be NULL when"
136 " creating an instance of the Dhcpv4Exchange");
137 }
138
139 // Create response message.
140 initResponse();
141 // Select subnet for the query message.
142 context_->subnet_ = subnet;
143 // Hardware address.
144 context_->hwaddr_ = query->getHWAddr();
145 // Pointer to client's query.
146 context_->query_ = query;
147
148 // If subnet found, retrieve client identifier which will be needed
149 // for allocations and search for reservations associated with a
150 // subnet/shared network.
151 if (subnet) {
152 OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
153 if (opt_clientid) {
154 context_->clientid_.reset(new ClientId(opt_clientid->getData()));
155 }
156
157 // Find static reservations if not disabled for our subnet.
158 if (subnet->getHostReservationMode() != Network::HR_DISABLED) {
159 // Before we can check for static reservations, we need to prepare a set
160 // of identifiers to be used for this.
161 setHostIdentifiers();
162
163 // Check for static reservations.
164 alloc_engine->findReservation(*context_);
165 }
166 }
167
168 // Set KNOWN builtin class if something was found, UNKNOWN if not.
169 if (!context_->hosts_.empty()) {
170 query->addClass("KNOWN");
171 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
172 .arg(query->getLabel())
173 .arg("KNOWN");
174 } else {
175 query->addClass("UNKNOWN");
176 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
177 .arg(query->getLabel())
178 .arg("UNKNOWN");
179 }
180
181 // Perform second pass of classification.
182 Dhcpv4Srv::evaluateClasses(query, true);
183
184 const ClientClasses& classes = query_->getClasses();
185 if (!classes.empty()) {
186 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
187 .arg(query_->getLabel())
188 .arg(classes.toText());
189 }
190};
191
192void
194 uint8_t resp_type = 0;
195 switch (getQuery()->getType()) {
196 case DHCPDISCOVER:
197 resp_type = DHCPOFFER;
198 break;
199 case DHCPREQUEST:
200 case DHCPINFORM:
201 resp_type = DHCPACK;
202 break;
203 default:
204 ;
205 }
206 // Only create a response if one is required.
207 if (resp_type > 0) {
208 resp_.reset(new Pkt4(resp_type, getQuery()->getTransid()));
209 copyDefaultFields();
210 copyDefaultOptions();
211
212 if (getQuery()->isDhcp4o6()) {
214 }
215 }
216}
217
218void
220 Pkt4o6Ptr query = boost::dynamic_pointer_cast<Pkt4o6>(getQuery());
221 if (!query) {
222 return;
223 }
224 const Pkt6Ptr& query6 = query->getPkt6();
225 Pkt6Ptr resp6(new Pkt6(DHCPV6_DHCPV4_RESPONSE, query6->getTransid()));
226 // Don't add client-id or server-id
227 // But copy relay info
228 if (!query6->relay_info_.empty()) {
229 resp6->copyRelayInfo(query6);
230 }
231 // Copy interface, and remote address and port
232 resp6->setIface(query6->getIface());
233 resp6->setIndex(query6->getIndex());
234 resp6->setRemoteAddr(query6->getRemoteAddr());
235 resp6->setRemotePort(query6->getRemotePort());
236 resp_.reset(new Pkt4o6(resp_, resp6));
237}
238
239void
240Dhcpv4Exchange::copyDefaultFields() {
241 resp_->setIface(query_->getIface());
242 resp_->setIndex(query_->getIndex());
243
244 // explicitly set this to 0
245 resp_->setSiaddr(IOAddress::IPV4_ZERO_ADDRESS());
246 // ciaddr is always 0, except for the Renew/Rebind state when it may
247 // be set to the ciaddr sent by the client.
248 resp_->setCiaddr(IOAddress::IPV4_ZERO_ADDRESS());
249 resp_->setHops(query_->getHops());
250
251 // copy MAC address
252 resp_->setHWAddr(query_->getHWAddr());
253
254 // relay address
255 resp_->setGiaddr(query_->getGiaddr());
256
257 // If src/dest HW addresses are used by the packet filtering class
258 // we need to copy them as well. There is a need to check that the
259 // address being set is not-NULL because an attempt to set the NULL
260 // HW would result in exception. If these values are not set, the
261 // the default HW addresses (zeroed) should be generated by the
262 // packet filtering class when creating Ethernet header for
263 // outgoing packet.
264 HWAddrPtr src_hw_addr = query_->getLocalHWAddr();
265 if (src_hw_addr) {
266 resp_->setLocalHWAddr(src_hw_addr);
267 }
268 HWAddrPtr dst_hw_addr = query_->getRemoteHWAddr();
269 if (dst_hw_addr) {
270 resp_->setRemoteHWAddr(dst_hw_addr);
271 }
272
273 // Copy flags from the request to the response per RFC 2131
274 resp_->setFlags(query_->getFlags());
275}
276
277void
278Dhcpv4Exchange::copyDefaultOptions() {
279 // Let's copy client-id to response. See RFC6842.
280 // It is possible to disable RFC6842 to keep backward compatibility
281 bool echo = CfgMgr::instance().getCurrentCfg()->getEchoClientId();
282 OptionPtr client_id = query_->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
283 if (client_id && echo) {
284 resp_->addOption(client_id);
285 }
286
287 // If this packet is relayed, we want to copy Relay Agent Info option
288 OptionPtr rai = query_->getOption(DHO_DHCP_AGENT_OPTIONS);
289 if (rai) {
290 resp_->addOption(rai);
291 }
292
293 // RFC 3011 states about the Subnet Selection Option
294
295 // "Servers configured to support this option MUST return an
296 // identical copy of the option to any client that sends it,
297 // regardless of whether or not the client requests the option in
298 // a parameter request list. Clients using this option MUST
299 // discard DHCPOFFER or DHCPACK packets that do not contain this
300 // option."
301 OptionPtr subnet_sel = query_->getOption(DHO_SUBNET_SELECTION);
302 if (subnet_sel) {
303 resp_->addOption(subnet_sel);
304 }
305}
306
307void
308Dhcpv4Exchange::setHostIdentifiers() {
309 const ConstCfgHostOperationsPtr cfg =
310 CfgMgr::instance().getCurrentCfg()->getCfgHostOperations4();
311
312 // Collect host identifiers. The identifiers are stored in order of preference.
313 // The server will use them in that order to search for host reservations.
314 BOOST_FOREACH(const Host::IdentifierType& id_type,
315 cfg->getIdentifierTypes()) {
316 switch (id_type) {
318 if (context_->hwaddr_ && !context_->hwaddr_->hwaddr_.empty()) {
319 context_->addHostIdentifier(id_type, context_->hwaddr_->hwaddr_);
320 }
321 break;
322
323 case Host::IDENT_DUID:
324 if (context_->clientid_) {
325 const std::vector<uint8_t>& vec = context_->clientid_->getDuid();
326 if (!vec.empty()) {
327 // Client identifier type = DUID? Client identifier holding a DUID
328 // comprises Type (1 byte), IAID (4 bytes), followed by the actual
329 // DUID. Thus, the minimal length is 6.
330 if ((vec[0] == CLIENT_ID_OPTION_TYPE_DUID) && (vec.size() > 5)) {
331 // Extract DUID, skip IAID.
332 context_->addHostIdentifier(id_type,
333 std::vector<uint8_t>(vec.begin() + 5,
334 vec.end()));
335 }
336 }
337 }
338 break;
339
341 {
342 OptionPtr rai = query_->getOption(DHO_DHCP_AGENT_OPTIONS);
343 if (rai) {
344 OptionPtr circuit_id_opt = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
345 if (circuit_id_opt) {
346 const OptionBuffer& circuit_id_vec = circuit_id_opt->getData();
347 if (!circuit_id_vec.empty()) {
348 context_->addHostIdentifier(id_type, circuit_id_vec);
349 }
350 }
351 }
352 }
353 break;
354
356 if (context_->clientid_) {
357 const std::vector<uint8_t>& vec = context_->clientid_->getDuid();
358 if (!vec.empty()) {
359 context_->addHostIdentifier(id_type, vec);
360 }
361 }
362 break;
363 case Host::IDENT_FLEX:
364 {
365 if (!HooksManager::calloutsPresent(Hooks.hook_index_host4_identifier_)) {
366 break;
367 }
368
369 CalloutHandlePtr callout_handle = getCalloutHandle(context_->query_);
370
372 std::vector<uint8_t> id;
373
374 // Use the RAII wrapper to make sure that the callout handle state is
375 // reset when this object goes out of scope. All hook points must do
376 // it to prevent possible circular dependency between the callout
377 // handle and its arguments.
378 ScopedCalloutHandleState callout_handle_state(callout_handle);
379
380 // Pass incoming packet as argument
381 callout_handle->setArgument("query4", context_->query_);
382 callout_handle->setArgument("id_type", type);
383 callout_handle->setArgument("id_value", id);
384
385 // Call callouts
386 HooksManager::callCallouts(Hooks.hook_index_host4_identifier_,
387 *callout_handle);
388
389 callout_handle->getArgument("id_type", type);
390 callout_handle->getArgument("id_value", id);
391
392 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_CONTINUE) &&
393 !id.empty()) {
394
396 .arg(Host::getIdentifierAsText(type, &id[0], id.size()));
397
398 context_->addHostIdentifier(type, id);
399 }
400 break;
401 }
402 default:
403 ;
404 }
405 }
406}
407
408void
410 if (context_->currentHost() && query_) {
411 const ClientClasses& classes = context_->currentHost()->getClientClasses4();
412 for (ClientClasses::const_iterator cclass = classes.cbegin();
413 cclass != classes.cend(); ++cclass) {
414 query_->addClass(*cclass);
415 }
416 }
417}
418
419void
421 ConstHostPtr host = context_->currentHost();
422 // Nothing to do if host reservations not specified for this client.
423 if (host) {
424 if (!host->getNextServer().isV4Zero()) {
425 resp_->setSiaddr(host->getNextServer());
426 }
427
428 std::string sname = host->getServerHostname();
429 if (!sname.empty()) {
430 resp_->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
431 sname.size());
432 }
433
434 std::string bootfile = host->getBootFileName();
435 if (!bootfile.empty()) {
436 resp_->setFile(reinterpret_cast<const uint8_t*>(bootfile.c_str()),
437 bootfile.size());
438 }
439 }
440}
441
442const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
443
444Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast,
445 const bool direct_response_desired)
446 : io_service_(new IOService()), shutdown_(true), alloc_engine_(), port_(port),
447 use_bcast_(use_bcast), network_state_(new NetworkState(NetworkState::DHCPv4)) {
448
449 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
450 try {
451 // Port 0 is used for testing purposes where we don't open broadcast
452 // capable sockets. So, set the packet filter handling direct traffic
453 // only if we are in non-test mode.
454 if (port) {
455 // First call to instance() will create IfaceMgr (it's a singleton)
456 // it may throw something if things go wrong.
457 // The 'true' value of the call to setMatchingPacketFilter imposes
458 // that IfaceMgr will try to use the mechanism to respond directly
459 // to the client which doesn't have address assigned. This capability
460 // may be lacking on some OSes, so there is no guarantee that server
461 // will be able to respond directly.
462 IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
463 }
464
465 // Instantiate allocation engine. The number of allocation attempts equal
466 // to zero indicates that the allocation engine will use the number of
467 // attempts depending on the pool size.
469 false /* false = IPv4 */));
470
472
473 } catch (const std::exception &e) {
474 LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what());
475 shutdown_ = true;
476 return;
477 }
478
479 shutdown_ = false;
480}
481
483 // Discard any cached packets or parked packets
485
486 try {
487 stopD2();
488 } catch(const std::exception& ex) {
489 // Highly unlikely, but lets Report it but go on
490 LOG_ERROR(dhcp4_logger, DHCP4_SRV_D2STOP_ERROR).arg(ex.what());
491 }
492
493 try {
495 } catch(const std::exception& ex) {
496 // Highly unlikely, but lets Report it but go on
497 LOG_ERROR(dhcp4_logger, DHCP4_SRV_DHCP4O6_ERROR).arg(ex.what());
498 }
499
501
502 // The lease manager was instantiated during DHCPv4Srv configuration,
503 // so we should clean up after ourselves.
505
506 // Explicitly unload hooks
508}
509
510void
512 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_SHUTDOWN_REQUEST);
513 shutdown_ = true;
514}
515
517Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query, bool& drop,
518 bool sanity_only) const {
519
520 // DHCPv4-over-DHCPv6 is a special (and complex) case
521 if (query->isDhcp4o6()) {
522 return (selectSubnet4o6(query, drop, sanity_only));
523 }
524
525 Subnet4Ptr subnet;
526
527 const SubnetSelector& selector = CfgSubnets4::initSelector(query);
528
529 CfgMgr& cfgmgr = CfgMgr::instance();
530 subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet(selector);
531
532 // Let's execute all callouts registered for subnet4_select
533 // (skip callouts if the selectSubnet was called to do sanity checks only)
534 if (!sanity_only &&
535 HooksManager::calloutsPresent(Hooks.hook_index_subnet4_select_)) {
536 CalloutHandlePtr callout_handle = getCalloutHandle(query);
537
538 // Use the RAII wrapper to make sure that the callout handle state is
539 // reset when this object goes out of scope. All hook points must do
540 // it to prevent possible circular dependency between the callout
541 // handle and its arguments.
542 ScopedCalloutHandleState callout_handle_state(callout_handle);
543
544 // Enable copying options from the packet within hook library.
545 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
546
547 // Set new arguments
548 callout_handle->setArgument("query4", query);
549 callout_handle->setArgument("subnet4", subnet);
550 callout_handle->setArgument("subnet4collection",
551 cfgmgr.getCurrentCfg()->
552 getCfgSubnets4()->getAll());
553
554 // Call user (and server-side) callouts
555 HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
556 *callout_handle);
557
558 // Callouts decided to skip this step. This means that no subnet
559 // will be selected. Packet processing will continue, but it will
560 // be severely limited (i.e. only global options will be assigned)
561 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
563 DHCP4_HOOK_SUBNET4_SELECT_SKIP)
564 .arg(query->getLabel());
565 return (Subnet4Ptr());
566 }
567
568 // Callouts decided to drop the packet. It is a superset of the
569 // skip case so no subnet will be selected.
570 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
572 DHCP4_HOOK_SUBNET4_SELECT_DROP)
573 .arg(query->getLabel());
574 drop = true;
575 return (Subnet4Ptr());
576 }
577
578 // Use whatever subnet was specified by the callout
579 callout_handle->getArgument("subnet4", subnet);
580 }
581
582 if (subnet) {
583 // Log at higher debug level that subnet has been found.
584 LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_SUBNET_SELECTED)
585 .arg(query->getLabel())
586 .arg(subnet->getID());
587 // Log detailed information about the selected subnet at the
588 // lower debug level.
590 .arg(query->getLabel())
591 .arg(subnet->toText());
592
593 } else {
595 DHCP4_SUBNET_SELECTION_FAILED)
596 .arg(query->getLabel());
597 }
598
599 return (subnet);
600}
601
603Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query, bool& drop,
604 bool sanity_only) const {
605
606 Subnet4Ptr subnet;
607
608 SubnetSelector selector;
609 selector.ciaddr_ = query->getCiaddr();
610 selector.giaddr_ = query->getGiaddr();
611 selector.local_address_ = query->getLocalAddr();
612 selector.client_classes_ = query->classes_;
613 selector.iface_name_ = query->getIface();
614 // Mark it as DHCPv4-over-DHCPv6
615 selector.dhcp4o6_ = true;
616 // Now the DHCPv6 part
617 selector.remote_address_ = query->getRemoteAddr();
618 selector.first_relay_linkaddr_ = IOAddress("::");
619
620 // Handle a DHCPv6 relayed query
621 Pkt4o6Ptr query4o6 = boost::dynamic_pointer_cast<Pkt4o6>(query);
622 if (!query4o6) {
623 isc_throw(Unexpected, "Can't get DHCP4o6 message");
624 }
625 const Pkt6Ptr& query6 = query4o6->getPkt6();
626
627 // Initialize fields specific to relayed messages.
628 if (query6 && !query6->relay_info_.empty()) {
629 BOOST_REVERSE_FOREACH(Pkt6::RelayInfo relay, query6->relay_info_) {
630 if (!relay.linkaddr_.isV6Zero() &&
631 !relay.linkaddr_.isV6LinkLocal()) {
632 selector.first_relay_linkaddr_ = relay.linkaddr_;
633 break;
634 }
635 }
636 selector.interface_id_ =
637 query6->getAnyRelayOption(D6O_INTERFACE_ID,
639 }
640
641 // If the Subnet Selection option is present, extract its value.
642 OptionPtr sbnsel = query->getOption(DHO_SUBNET_SELECTION);
643 if (sbnsel) {
644 OptionCustomPtr oc = boost::dynamic_pointer_cast<OptionCustom>(sbnsel);
645 if (oc) {
646 selector.option_select_ = oc->readAddress();
647 }
648 }
649
650 CfgMgr& cfgmgr = CfgMgr::instance();
651 subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet4o6(selector);
652
653 // Let's execute all callouts registered for subnet4_select.
654 // (skip callouts if the selectSubnet was called to do sanity checks only)
655 if (!sanity_only &&
656 HooksManager::calloutsPresent(Hooks.hook_index_subnet4_select_)) {
657 CalloutHandlePtr callout_handle = getCalloutHandle(query);
658
659 // Use the RAII wrapper to make sure that the callout handle state is
660 // reset when this object goes out of scope. All hook points must do
661 // it to prevent possible circular dependency between the callout
662 // handle and its arguments.
663 ScopedCalloutHandleState callout_handle_state(callout_handle);
664
665 // Set new arguments
666 callout_handle->setArgument("query4", query);
667 callout_handle->setArgument("subnet4", subnet);
668 callout_handle->setArgument("subnet4collection",
669 cfgmgr.getCurrentCfg()->
670 getCfgSubnets4()->getAll());
671
672 // Call user (and server-side) callouts
673 HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
674 *callout_handle);
675
676 // Callouts decided to skip this step. This means that no subnet
677 // will be selected. Packet processing will continue, but it will
678 // be severely limited (i.e. only global options will be assigned)
679 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
681 DHCP4_HOOK_SUBNET4_SELECT_SKIP)
682 .arg(query->getLabel());
683 return (Subnet4Ptr());
684 }
685
686 // Callouts decided to drop the packet. It is a superset of the
687 // skip case so no subnet will be selected.
688 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
690 DHCP4_HOOK_SUBNET4_SELECT_DROP)
691 .arg(query->getLabel());
692 drop = true;
693 return (Subnet4Ptr());
694 }
695
696 // Use whatever subnet was specified by the callout
697 callout_handle->getArgument("subnet4", subnet);
698 }
699
700 if (subnet) {
701 // Log at higher debug level that subnet has been found.
702 LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_SUBNET_SELECTED)
703 .arg(query->getLabel())
704 .arg(subnet->getID());
705 // Log detailed information about the selected subnet at the
706 // lower debug level.
708 .arg(query->getLabel())
709 .arg(subnet->toText());
710
711 } else {
713 DHCP4_SUBNET_SELECTION_FAILED)
714 .arg(query->getLabel());
715 }
716
717 return (subnet);
718}
719
722 return (IfaceMgr::instance().receive4(timeout));
723}
724
725void
727 IfaceMgr::instance().send(packet);
728}
729
730bool
732 while (!shutdown_) {
733 try {
734 run_one();
735 getIOService()->poll();
736 } catch (const std::exception& e) {
737 // General catch-all exception that are not caught by more specific
738 // catches. This one is for exceptions derived from std::exception.
739 LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_STD_EXCEPTION)
740 .arg(e.what());
741 } catch (...) {
742 // General catch-all exception that are not caught by more specific
743 // catches. This one is for other exceptions, not derived from
744 // std::exception.
745 LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_EXCEPTION);
746 }
747 }
748
749 return (true);
750}
751
752void
754 // client's message and server's response
755 Pkt4Ptr query;
756 Pkt4Ptr rsp;
757
758 try {
759 // Set select() timeout to 1s. This value should not be modified
760 // because it is important that the select() returns control
761 // frequently so as the IOService can be polled for ready handlers.
762 uint32_t timeout = 1;
763 query = receivePacket(timeout);
764
765 // Log if packet has arrived. We can't log the detailed information
766 // about the DHCP message because it hasn't been unpacked/parsed
767 // yet, and it can't be parsed at this point because hooks will
768 // have to process it first. The only information available at this
769 // point are: the interface, source address and destination addresses
770 // and ports.
771 if (query) {
772 LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_BUFFER_RECEIVED)
773 .arg(query->getRemoteAddr().toText())
774 .arg(query->getRemotePort())
775 .arg(query->getLocalAddr().toText())
776 .arg(query->getLocalPort())
777 .arg(query->getIface());
778
779 }
780 // We used to log that the wait was interrupted, but this is no longer
781 // the case. Our wait time is 1s now, so the lack of query packet more
782 // likely means that nothing new appeared within a second, rather than
783 // we were interrupted. And we don't want to print a message every
784 // second.
785
786 } catch (const SignalInterruptOnSelect&) {
787 // Packet reception interrupted because a signal has been received.
788 // This is not an error because we might have received a SIGTERM,
789 // SIGINT, SIGHUP or SIGCHLD which are handled by the server. For
790 // signals that are not handled by the server we rely on the default
791 // behavior of the system.
792 LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT_SIGNAL)
793 .arg(signal_set_->getNext());
794 } catch (const std::exception& e) {
795 // Log all other errors.
796 LOG_ERROR(packet4_logger, DHCP4_BUFFER_RECEIVE_FAIL).arg(e.what());
797 }
798
799 // Handle next signal received by the process. It must be called after
800 // an attempt to receive a packet to properly handle server shut down.
801 // The SIGTERM or SIGINT will be received prior to, or during execution
802 // of select() (select is invoked by receivePacket()). When that
803 // happens, select will be interrupted. The signal handler will be
804 // invoked immediately after select(). The handler will set the
805 // shutdown flag and cause the process to terminate before the next
806 // select() function is called. If the function was called before
807 // receivePacket the process could wait up to the duration of timeout
808 // of select() to terminate.
809 try {
810 handleSignal();
811 } catch (const std::exception& e) {
812 // Standard exception occurred. Let's be on the safe side to
813 // catch std::exception.
814 LOG_ERROR(dhcp4_logger, DHCP4_HANDLE_SIGNAL_EXCEPTION)
815 .arg(e.what());
816 }
817
818 // Timeout may be reached or signal received, which breaks select()
819 // with no reception occurred. No need to log anything here because
820 // we have logged right after the call to receivePacket().
821 if (!query) {
822 return;
823 }
824
825 // If the DHCP service has been globally disabled, drop the packet.
826 if (!network_state_->isServiceEnabled()) {
828 DHCP4_PACKET_DROP_0008)
829 .arg(query->getLabel());
830 return;
831 } else {
832 processPacket(query, rsp);
833 }
834
835 if (!rsp) {
836 return;
837 }
838
839 CalloutHandlePtr callout_handle = getCalloutHandle(query);
840 processPacketBufferSend(callout_handle, rsp);
841}
842
843void
844Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp, bool allow_packet_park) {
845 // Log reception of the packet. We need to increase it early, as any
846 // failures in unpacking will cause the packet to be dropped. We
847 // will increase type specific statistic further down the road.
848 // See processStatsReceived().
850 static_cast<int64_t>(1));
851
852 bool skip_unpack = false;
853
854 // The packet has just been received so contains the uninterpreted wire
855 // data; execute callouts registered for buffer4_receive.
856 if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
857 CalloutHandlePtr callout_handle = getCalloutHandle(query);
858
859 // Use the RAII wrapper to make sure that the callout handle state is
860 // reset when this object goes out of scope. All hook points must do
861 // it to prevent possible circular dependency between the callout
862 // handle and its arguments.
863 ScopedCalloutHandleState callout_handle_state(callout_handle);
864
865 // Enable copying options from the packet within hook library.
866 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
867
868 // Pass incoming packet as argument
869 callout_handle->setArgument("query4", query);
870
871 // Call callouts
872 HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
873 *callout_handle);
874
875 // Callouts decided to drop the received packet.
876 // The response (rsp) is null so the caller (run_one) will
877 // immediately return too.
878 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
880 DHCP4_HOOK_BUFFER_RCVD_DROP)
881 .arg(query->getRemoteAddr().toText())
882 .arg(query->getLocalAddr().toText())
883 .arg(query->getIface());
884 return;
885 }
886
887 // Callouts decided to skip the next processing step. The next
888 // processing step would to parse the packet, so skip at this
889 // stage means that callouts did the parsing already, so server
890 // should skip parsing.
891 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
893 DHCP4_HOOK_BUFFER_RCVD_SKIP)
894 .arg(query->getRemoteAddr().toText())
895 .arg(query->getLocalAddr().toText())
896 .arg(query->getIface());
897 skip_unpack = true;
898 }
899
900 callout_handle->getArgument("query4", query);
901 }
902
903 // Unpack the packet information unless the buffer4_receive callouts
904 // indicated they did it
905 if (!skip_unpack) {
906 try {
907 LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_UNPACK)
908 .arg(query->getRemoteAddr().toText())
909 .arg(query->getLocalAddr().toText())
910 .arg(query->getIface());
911 query->unpack();
912 } catch (const SkipRemainingOptionsError& e) {
913 // An option failed to unpack but we are to attempt to process it
914 // anyway. Log it and let's hope for the best.
916 DHCP4_PACKET_OPTIONS_SKIPPED)
917 .arg(e.what());
918 } catch (const std::exception& e) {
919 // Failed to parse the packet.
921 DHCP4_PACKET_DROP_0001)
922 .arg(query->getRemoteAddr().toText())
923 .arg(query->getLocalAddr().toText())
924 .arg(query->getIface())
925 .arg(e.what());
926
927 // Increase the statistics of parse failures and dropped packets.
928 isc::stats::StatsMgr::instance().addValue("pkt4-parse-failed",
929 static_cast<int64_t>(1));
930 isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
931 static_cast<int64_t>(1));
932 return;
933 }
934 }
935
936 // Update statistics accordingly for received packet.
938
939 // Assign this packet to one or more classes if needed. We need to do
940 // this before calling accept(), because getSubnet4() may need client
941 // class information.
942 classifyPacket(query);
943
944 // Now it is classified the deferred unpacking can be done.
945 deferredUnpack(query);
946
947 // Check whether the message should be further processed or discarded.
948 // There is no need to log anything here. This function logs by itself.
949 if (!accept(query)) {
950 // Increase the statistic of dropped packets.
951 isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
952 static_cast<int64_t>(1));
953 return;
954 }
955
956 // We have sanity checked (in accept() that the Message Type option
957 // exists, so we can safely get it here.
958 int type = query->getType();
959 LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_PACKET_RECEIVED)
960 .arg(query->getLabel())
961 .arg(query->getName())
962 .arg(type)
963 .arg(query->getRemoteAddr())
964 .arg(query->getLocalAddr())
965 .arg(query->getIface());
967 .arg(query->getLabel())
968 .arg(query->toText());
969
970 // Let's execute all callouts registered for pkt4_receive
971 if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_receive_)) {
972 CalloutHandlePtr callout_handle = getCalloutHandle(query);
973
974 // Use the RAII wrapper to make sure that the callout handle state is
975 // reset when this object goes out of scope. All hook points must do
976 // it to prevent possible circular dependency between the callout
977 // handle and its arguments.
978 ScopedCalloutHandleState callout_handle_state(callout_handle);
979
980 // Enable copying options from the packet within hook library.
981 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
982
983 // Pass incoming packet as argument
984 callout_handle->setArgument("query4", query);
985
986 // Call callouts
987 HooksManager::callCallouts(Hooks.hook_index_pkt4_receive_,
988 *callout_handle);
989
990 // Callouts decided to skip the next processing step. The next
991 // processing step would to process the packet, so skip at this
992 // stage means drop.
993 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
994 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
996 DHCP4_HOOK_PACKET_RCVD_SKIP)
997 .arg(query->getLabel());
998 return;
999 }
1000
1001 callout_handle->getArgument("query4", query);
1002 }
1003
1005
1006 try {
1007 switch (query->getType()) {
1008 case DHCPDISCOVER:
1009 rsp = processDiscover(query);
1010 break;
1011
1012 case DHCPREQUEST:
1013 // Note that REQUEST is used for many things in DHCPv4: for
1014 // requesting new leases, renewing existing ones and even
1015 // for rebinding.
1016 rsp = processRequest(query, ctx);
1017 break;
1018
1019 case DHCPRELEASE:
1020 processRelease(query, ctx);
1021 break;
1022
1023 case DHCPDECLINE:
1024 processDecline(query, ctx);
1025 break;
1026
1027 case DHCPINFORM:
1028 rsp = processInform(query);
1029 break;
1030
1031 default:
1032 // Only action is to output a message if debug is enabled,
1033 // and that is covered by the debug statement before the
1034 // "switch" statement.
1035 ;
1036 }
1037 } catch (const std::exception& e) {
1038
1039 // Catch-all exception (we used to call only isc::Exception, but
1040 // std::exception could potentially be raised and if we don't catch
1041 // it here, it would be caught in main() and the process would
1042 // terminate). Just log the problem and ignore the packet.
1043 // (The problem is logged as a debug message because debug is
1044 // disabled by default - it prevents a DDOS attack based on the
1045 // sending of problem packets.)
1047 DHCP4_PACKET_DROP_0007)
1048 .arg(query->getLabel())
1049 .arg(e.what());
1050
1051 // Increase the statistic of dropped packets.
1052 isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
1053 static_cast<int64_t>(1));
1054 }
1055
1056 bool packet_park = false;
1057
1058 if (ctx && HooksManager::calloutsPresent(Hooks.hook_index_leases4_committed_)) {
1059 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1060
1061 // Use the RAII wrapper to make sure that the callout handle state is
1062 // reset when this object goes out of scope. All hook points must do
1063 // it to prevent possible circular dependency between the callout
1064 // handle and its arguments.
1065 ScopedCalloutHandleState callout_handle_state(callout_handle);
1066
1067 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
1068
1069 // Also pass the corresponding query packet as argument
1070 callout_handle->setArgument("query4", query);
1071
1072 Lease4CollectionPtr new_leases(new Lease4Collection());
1073 if (ctx->new_lease_) {
1074 new_leases->push_back(ctx->new_lease_);
1075 }
1076 callout_handle->setArgument("leases4", new_leases);
1077
1078 Lease4CollectionPtr deleted_leases(new Lease4Collection());
1079 if (ctx->old_lease_) {
1080 if ((!ctx->new_lease_) || (ctx->new_lease_->addr_ != ctx->old_lease_->addr_)) {
1081 deleted_leases->push_back(ctx->old_lease_);
1082 }
1083 }
1084 callout_handle->setArgument("deleted_leases4", deleted_leases);
1085
1086 // Call all installed callouts
1087 HooksManager::callCallouts(Hooks.hook_index_leases4_committed_,
1088 *callout_handle);
1089
1090 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1092 DHCP4_HOOK_LEASES4_COMMITTED_DROP)
1093 .arg(query->getLabel());
1094 rsp.reset();
1095
1096 } else if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK)
1097 && allow_packet_park) {
1098 packet_park = true;
1099 }
1100 }
1101
1102 if (!rsp) {
1103 return;
1104 }
1105
1106 // PARKING SPOT after leases4_committed hook point.
1107 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1108 if (packet_park) {
1110 DHCP4_HOOK_LEASES4_COMMITTED_PARK)
1111 .arg(query->getLabel());
1112
1113 // Park the packet. The function we bind here will be executed when the hook
1114 // library unparks the packet.
1115 HooksManager::park("leases4_committed", query,
1116 [this, callout_handle, query, rsp]() mutable {
1117 processPacketPktSend(callout_handle, query, rsp);
1118 processPacketBufferSend(callout_handle, rsp);
1119 });
1120
1121 // If we have parked the packet, let's reset the pointer to the
1122 // response to indicate to the caller that it should return, as
1123 // the packet processing will continue via the callback.
1124 rsp.reset();
1125
1126 } else {
1127 processPacketPktSend(callout_handle, query, rsp);
1128 }
1129}
1130
1131void
1133 Pkt4Ptr& query, Pkt4Ptr& rsp) {
1134 if (!rsp) {
1135 return;
1136 }
1137
1138 // Specifies if server should do the packing
1139 bool skip_pack = false;
1140
1141 // Execute all callouts registered for pkt4_send
1142 if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_send_)) {
1143
1144 // Use the RAII wrapper to make sure that the callout handle state is
1145 // reset when this object goes out of scope. All hook points must do
1146 // it to prevent possible circular dependency between the callout
1147 // handle and its arguments.
1148 ScopedCalloutHandleState callout_handle_state(callout_handle);
1149
1150 // Enable copying options from the query and response packets within
1151 // hook library.
1152 ScopedEnableOptionsCopy<Pkt4> query_resp_options_copy(query, rsp);
1153
1154 // Set our response
1155 callout_handle->setArgument("response4", rsp);
1156
1157 // Also pass the corresponding query packet as argument
1158 callout_handle->setArgument("query4", query);
1159
1160 // Call all installed callouts
1161 HooksManager::callCallouts(Hooks.hook_index_pkt4_send_,
1162 *callout_handle);
1163
1164 // Callouts decided to skip the next processing step. The next
1165 // processing step would to send the packet, so skip at this
1166 // stage means "drop response".
1167 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1168 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1170 DHCP4_HOOK_PACKET_SEND_SKIP)
1171 .arg(query->getLabel());
1172 skip_pack = true;
1173 }
1174 }
1175
1176 if (!skip_pack) {
1177 try {
1178 LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_PACK)
1179 .arg(rsp->getLabel());
1180 rsp->pack();
1181 } catch (const std::exception& e) {
1182 LOG_ERROR(options4_logger, DHCP4_PACKET_PACK_FAIL)
1183 .arg(rsp->getLabel())
1184 .arg(e.what());
1185 }
1186 }
1187}
1188
1189void
1191 Pkt4Ptr& rsp) {
1192 if (!rsp) {
1193 return;
1194 }
1195
1196 try {
1197 // Now all fields and options are constructed into output wire buffer.
1198 // Option objects modification does not make sense anymore. Hooks
1199 // can only manipulate wire buffer at this stage.
1200 // Let's execute all callouts registered for buffer4_send
1201 if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
1202
1203 // Use the RAII wrapper to make sure that the callout handle state is
1204 // reset when this object goes out of scope. All hook points must do
1205 // it to prevent possible circular dependency between the callout
1206 // handle and its arguments.
1207 ScopedCalloutHandleState callout_handle_state(callout_handle);
1208
1209 // Enable copying options from the packet within hook library.
1210 ScopedEnableOptionsCopy<Pkt4> resp4_options_copy(rsp);
1211
1212 // Pass incoming packet as argument
1213 callout_handle->setArgument("response4", rsp);
1214
1215 // Call callouts
1216 HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
1217 *callout_handle);
1218
1219 // Callouts decided to skip the next processing step. The next
1220 // processing step would to parse the packet, so skip at this
1221 // stage means drop.
1222 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1223 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1225 DHCP4_HOOK_BUFFER_SEND_SKIP)
1226 .arg(rsp->getLabel());
1227 return;
1228 }
1229
1230 callout_handle->getArgument("response4", rsp);
1231 }
1232
1233 LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_PACKET_SEND)
1234 .arg(rsp->getLabel())
1235 .arg(rsp->getName())
1236 .arg(static_cast<int>(rsp->getType()))
1237 .arg(rsp->getLocalAddr().isV4Zero() ? "*" : rsp->getLocalAddr().toText())
1238 .arg(rsp->getLocalPort())
1239 .arg(rsp->getRemoteAddr())
1240 .arg(rsp->getRemotePort())
1241 .arg(rsp->getIface().empty() ? "to be determined from routing" :
1242 rsp->getIface());
1243
1245 DHCP4_RESPONSE_DATA)
1246 .arg(rsp->getLabel())
1247 .arg(rsp->getName())
1248 .arg(static_cast<int>(rsp->getType()))
1249 .arg(rsp->toText());
1250 sendPacket(rsp);
1251
1252 // Update statistics accordingly for sent packet.
1253 processStatsSent(rsp);
1254
1255 } catch (const std::exception& e) {
1256 LOG_ERROR(packet4_logger, DHCP4_PACKET_SEND_FAIL)
1257 .arg(rsp->getLabel())
1258 .arg(e.what());
1259 }
1260}
1261
1262string
1264 if (!srvid) {
1265 isc_throw(BadValue, "NULL pointer passed to srvidToString()");
1266 }
1267 boost::shared_ptr<Option4AddrLst> generated =
1268 boost::dynamic_pointer_cast<Option4AddrLst>(srvid);
1269 if (!srvid) {
1270 isc_throw(BadValue, "Pointer to invalid option passed to srvidToString()");
1271 }
1272
1273 Option4AddrLst::AddressContainer addrs = generated->getAddresses();
1274 if (addrs.size() != 1) {
1275 isc_throw(BadValue, "Malformed option passed to srvidToString(). "
1276 << "Expected to contain a single IPv4 address.");
1277 }
1278
1279 return (addrs[0].toText());
1280}
1281
1282void
1284
1285 // Do not append generated server identifier if there is one appended already.
1286 // This is when explicitly configured server identifier option is present.
1287 if (ex.getResponse()->getOption(DHO_DHCP_SERVER_IDENTIFIER)) {
1288 return;
1289 }
1290
1291 // Use local address on which the packet has been received as a
1292 // server identifier. In some cases it may be a different address,
1293 // e.g. broadcast packet or DHCPv4o6 packet.
1294 IOAddress local_addr = ex.getQuery()->getLocalAddr();
1295 Pkt4Ptr query = ex.getQuery();
1296
1297 if (local_addr.isV4Bcast() || query->isDhcp4o6()) {
1298 SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
1299 local_addr = sock_info.addr_;
1300 }
1301
1303 local_addr));
1304 ex.getResponse()->addOption(opt_srvid);
1305}
1306
1307void
1309 CfgOptionList& co_list = ex.getCfgOptionList();
1310
1311 // Retrieve subnet.
1312 Subnet4Ptr subnet = ex.getContext()->subnet_;
1313 if (!subnet) {
1314 // All methods using the CfgOptionList object return soon when
1315 // there is no subnet so do the same
1316 return;
1317 }
1318
1319 // Firstly, host specific options.
1320 const ConstHostPtr& host = ex.getContext()->currentHost();
1321 if (host && !host->getCfgOption4()->empty()) {
1322 co_list.push_back(host->getCfgOption4());
1323 }
1324
1325 // Secondly, pool specific options.
1326 Pkt4Ptr resp = ex.getResponse();
1328 if (resp) {
1329 addr = resp->getYiaddr();
1330 }
1331 if (!addr.isV4Zero()) {
1332 PoolPtr pool = subnet->getPool(Lease::TYPE_V4, addr, false);
1333 if (pool && !pool->getCfgOption()->empty()) {
1334 co_list.push_back(pool->getCfgOption());
1335 }
1336 }
1337
1338 // Thirdly, subnet configured options.
1339 if (!subnet->getCfgOption()->empty()) {
1340 co_list.push_back(subnet->getCfgOption());
1341 }
1342
1343 // Forthly, shared network specific options.
1344 SharedNetwork4Ptr network;
1345 subnet->getSharedNetwork(network);
1346 if (network && !network->getCfgOption()->empty()) {
1347 co_list.push_back(network->getCfgOption());
1348 }
1349
1350 // Each class in the incoming packet
1351 const ClientClasses& classes = ex.getQuery()->getClasses();
1352 for (ClientClasses::const_iterator cclass = classes.cbegin();
1353 cclass != classes.cend(); ++cclass) {
1354 // Find the client class definition for this class
1356 getClientClassDictionary()->findClass(*cclass);
1357 if (!ccdef) {
1358 // Not found: the class is built-in or not configured
1359 if (!isClientClassBuiltIn(*cclass)) {
1360 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNCONFIGURED)
1361 .arg(ex.getQuery()->getLabel())
1362 .arg(*cclass);
1363 }
1364 // Skip it
1365 continue;
1366 }
1367 if (ccdef->getCfgOption()->empty()) {
1368 // Skip classes which don't configure options
1369 continue;
1370 }
1371 co_list.push_back(ccdef->getCfgOption());
1372 }
1373
1374 // Last global options
1375 if (!CfgMgr::instance().getCurrentCfg()->getCfgOption()->empty()) {
1376 co_list.push_back(CfgMgr::instance().getCurrentCfg()->getCfgOption());
1377 }
1378}
1379
1380void
1382 // Get the subnet relevant for the client. We will need it
1383 // to get the options associated with it.
1384 Subnet4Ptr subnet = ex.getContext()->subnet_;
1385 // If we can't find the subnet for the client there is no way
1386 // to get the options to be sent to a client. We don't log an
1387 // error because it will be logged by the assignLease method
1388 // anyway.
1389 if (!subnet) {
1390 return;
1391 }
1392
1393 // Unlikely short cut
1394 const CfgOptionList& co_list = ex.getCfgOptionList();
1395 if (co_list.empty()) {
1396 return;
1397 }
1398
1399 Pkt4Ptr query = ex.getQuery();
1400 Pkt4Ptr resp = ex.getResponse();
1401 std::vector<uint8_t> requested_opts;
1402
1403 // try to get the 'Parameter Request List' option which holds the
1404 // codes of requested options.
1405 OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast<
1407 // Get the codes of requested options.
1408 if (option_prl) {
1409 requested_opts = option_prl->getValues();
1410 }
1411 // Iterate on the configured option list to add persistent options
1412 for (CfgOptionList::const_iterator copts = co_list.begin();
1413 copts != co_list.end(); ++copts) {
1414 const OptionContainerPtr& opts = (*copts)->getAll(DHCP4_OPTION_SPACE);
1415 if (!opts) {
1416 continue;
1417 }
1418 // Get persistent options
1419 const OptionContainerPersistIndex& idx = opts->get<2>();
1420 const OptionContainerPersistRange& range = idx.equal_range(true);
1421 for (OptionContainerPersistIndex::const_iterator desc = range.first;
1422 desc != range.second; ++desc) {
1423 // Add the persistent option code to requested options
1424 if (desc->option_) {
1425 uint8_t code = static_cast<uint8_t>(desc->option_->getType());
1426 requested_opts.push_back(code);
1427 }
1428 }
1429 }
1430
1431 // For each requested option code get the instance of the option
1432 // to be returned to the client.
1433 for (std::vector<uint8_t>::const_iterator opt = requested_opts.begin();
1434 opt != requested_opts.end(); ++opt) {
1435 // Add nothing when it is already there
1436 if (!resp->getOption(*opt)) {
1437 // Iterate on the configured option list
1438 for (CfgOptionList::const_iterator copts = co_list.begin();
1439 copts != co_list.end(); ++copts) {
1440 OptionDescriptor desc = (*copts)->get(DHCP4_OPTION_SPACE, *opt);
1441 // Got it: add it and jump to the outer loop
1442 if (desc.option_) {
1443 resp->addOption(desc.option_);
1444 break;
1445 }
1446 }
1447 }
1448 }
1449}
1450
1451void
1453 // Get the configured subnet suitable for the incoming packet.
1454 Subnet4Ptr subnet = ex.getContext()->subnet_;
1455 // Leave if there is no subnet matching the incoming packet.
1456 // There is no need to log the error message here because
1457 // it will be logged in the assignLease() when it fails to
1458 // pick the suitable subnet. We don't want to duplicate
1459 // error messages in such case.
1460 if (!subnet) {
1461 return;
1462 }
1463
1464 // Unlikely short cut
1465 const CfgOptionList& co_list = ex.getCfgOptionList();
1466 if (co_list.empty()) {
1467 return;
1468 }
1469
1470 // Try to get the vendor option
1471 boost::shared_ptr<OptionVendor> vendor_req = boost::dynamic_pointer_cast<
1472 OptionVendor>(ex.getQuery()->getOption(DHO_VIVSO_SUBOPTIONS));
1473 if (!vendor_req) {
1474 return;
1475 }
1476
1477 uint32_t vendor_id = vendor_req->getVendorId();
1478 std::vector<uint8_t> requested_opts;
1479
1480 // Let's try to get ORO within that vendor-option
1484 boost::dynamic_pointer_cast<OptionUint8Array>(vendor_req->getOption(DOCSIS3_V4_ORO));
1485 // Get the list of options that client requested.
1486 if (oro) {
1487 requested_opts = oro->getValues();
1488 }
1489 // Iterate on the configured option list to add persistent options
1490 for (CfgOptionList::const_iterator copts = co_list.begin();
1491 copts != co_list.end(); ++copts) {
1492 const OptionContainerPtr& opts = (*copts)->getAll(vendor_id);
1493 if (!opts) {
1494 continue;
1495 }
1496 // Get persistent options
1497 const OptionContainerPersistIndex& idx = opts->get<2>();
1498 const OptionContainerPersistRange& range = idx.equal_range(true);
1499 for (OptionContainerPersistIndex::const_iterator desc = range.first;
1500 desc != range.second; ++desc) {
1501 // Add the persistent option code to requested options
1502 if (desc->option_) {
1503 uint8_t code = static_cast<uint8_t>(desc->option_->getType());
1504 requested_opts.push_back(code);
1505 }
1506 }
1507 }
1508
1509 // If there is nothing to add don't do anything then.
1510 if (requested_opts.empty()) {
1511 return;
1512 }
1513
1514 boost::shared_ptr<OptionVendor> vendor_rsp(new OptionVendor(Option::V4, vendor_id));
1515
1516 // Get the list of options that client requested.
1517 bool added = false;
1518 for (std::vector<uint8_t>::const_iterator code = requested_opts.begin();
1519 code != requested_opts.end(); ++code) {
1520 if (!vendor_rsp->getOption(*code)) {
1521 for (CfgOptionList::const_iterator copts = co_list.begin();
1522 copts != co_list.end(); ++copts) {
1523 OptionDescriptor desc = (*copts)->get(vendor_id, *code);
1524 if (desc.option_) {
1525 vendor_rsp->addOption(desc.option_);
1526 added = true;
1527 break;
1528 }
1529 }
1530 }
1531
1532 if (added) {
1533 ex.getResponse()->addOption(vendor_rsp);
1534 }
1535 }
1536}
1537
1538
1539void
1541 // Identify options that we always want to send to the
1542 // client (if they are configured).
1543 static const uint16_t required_options[] = {
1548
1549 static size_t required_options_size =
1550 sizeof(required_options) / sizeof(required_options[0]);
1551
1552 // Get the subnet.
1553 Subnet4Ptr subnet = ex.getContext()->subnet_;
1554 if (!subnet) {
1555 return;
1556 }
1557
1558 // Unlikely short cut
1559 const CfgOptionList& co_list = ex.getCfgOptionList();
1560 if (co_list.empty()) {
1561 return;
1562 }
1563
1564 Pkt4Ptr resp = ex.getResponse();
1565
1566 // Try to find all 'required' options in the outgoing
1567 // message. Those that are not present will be added.
1568 for (int i = 0; i < required_options_size; ++i) {
1569 OptionPtr opt = resp->getOption(required_options[i]);
1570 if (!opt) {
1571 // Check whether option has been configured.
1572 for (CfgOptionList::const_iterator copts = co_list.begin();
1573 copts != co_list.end(); ++copts) {
1574 OptionDescriptor desc = (*copts)->get(DHCP4_OPTION_SPACE,
1575 required_options[i]);
1576 if (desc.option_) {
1577 resp->addOption(desc.option_);
1578 break;
1579 }
1580 }
1581 }
1582 }
1583}
1584
1585void
1587 // It is possible that client has sent both Client FQDN and Hostname
1588 // option. In such case, server should prefer Client FQDN option and
1589 // ignore the Hostname option.
1590 try {
1591 Pkt4Ptr resp = ex.getResponse();
1592 Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn>
1593 (ex.getQuery()->getOption(DHO_FQDN));
1594 if (fqdn) {
1595 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENT_FQDN_PROCESS)
1596 .arg(ex.getQuery()->getLabel());
1597 processClientFqdnOption(ex);
1598
1599 } else {
1601 DHCP4_CLIENT_HOSTNAME_PROCESS)
1602 .arg(ex.getQuery()->getLabel());
1603 processHostnameOption(ex);
1604 }
1605 } catch (const Exception& e) {
1606 // In some rare cases it is possible that the client's name processing
1607 // fails. For example, the Hostname option may be malformed, or there
1608 // may be an error in the server's logic which would cause multiple
1609 // attempts to add the same option to the response message. This
1610 // error message aggregates all these errors so they can be diagnosed
1611 // from the log. We don't want to throw an exception here because,
1612 // it will impact the processing of the whole packet. We rather want
1613 // the processing to continue, even if the client's name is wrong.
1614 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_NAME_PROC_FAIL)
1615 .arg(ex.getQuery()->getLabel())
1616 .arg(e.what());
1617 }
1618}
1619
1620void
1621Dhcpv4Srv::processClientFqdnOption(Dhcpv4Exchange& ex) {
1622 // Obtain the FQDN option from the client's message.
1623 Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
1624 Option4ClientFqdn>(ex.getQuery()->getOption(DHO_FQDN));
1625
1626 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_FQDN_DATA)
1627 .arg(ex.getQuery()->getLabel())
1628 .arg(fqdn->toText());
1629
1630 // Create the DHCPv4 Client FQDN Option to be included in the server's
1631 // response to a client.
1632 Option4ClientFqdnPtr fqdn_resp(new Option4ClientFqdn(*fqdn));
1633
1634 // Set the server S, N, and O flags based on client's flags and
1635 // current configuration.
1637 d2_mgr.adjustFqdnFlags<Option4ClientFqdn>(*fqdn, *fqdn_resp);
1638
1639 // Carry over the client's E flag.
1642
1643 if (ex.getContext()->currentHost() &&
1644 !ex.getContext()->currentHost()->getHostname().empty()) {
1646 fqdn_resp->setDomainName(d2_mgr.qualifyName(ex.getContext()->currentHost()->getHostname(),
1648
1649 } else {
1650 // Adjust the domain name based on domain name value and type sent by the
1651 // client and current configuration.
1652 d2_mgr.adjustDomainName<Option4ClientFqdn>(*fqdn, *fqdn_resp);
1653 }
1654
1655 // Add FQDN option to the response message. Note that, there may be some
1656 // cases when server may choose not to include the FQDN option in a
1657 // response to a client. In such cases, the FQDN should be removed from the
1658 // outgoing message. In theory we could cease to include the FQDN option
1659 // in this function until it is confirmed that it should be included.
1660 // However, we include it here for simplicity. Functions used to acquire
1661 // lease for a client will scan the response message for FQDN and if it
1662 // is found they will take necessary actions to store the FQDN information
1663 // in the lease database as well as to generate NameChangeRequests to DNS.
1664 // If we don't store the option in the response message, we will have to
1665 // propagate it in the different way to the functions which acquire the
1666 // lease. This would require modifications to the API of this class.
1667 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_RESPONSE_FQDN_DATA)
1668 .arg(ex.getQuery()->getLabel())
1669 .arg(fqdn_resp->toText());
1670 ex.getResponse()->addOption(fqdn_resp);
1671}
1672
1673void
1674Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) {
1675 // Fetch D2 configuration.
1677
1678 // Obtain the Hostname option from the client's message.
1679 OptionStringPtr opt_hostname = boost::dynamic_pointer_cast<OptionString>
1680 (ex.getQuery()->getOption(DHO_HOST_NAME));
1681
1682 if (opt_hostname) {
1683 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_HOSTNAME_DATA)
1684 .arg(ex.getQuery()->getLabel())
1685 .arg(opt_hostname->getValue());
1686 }
1687
1689
1690 // Hostname reservations take precedence over any other configuration,
1691 // i.e. DDNS configuration.
1692 if (ctx->currentHost() && !ctx->currentHost()->getHostname().empty()) {
1693 // In order to send a reserved hostname value we expect that at least
1694 // one of the following is the case: the client has sent us a hostname
1695 // option, or the client has sent Parameter Request List option with
1696 // the hostname option code included.
1697
1698 // It is going to be less expensive to first check the presence of the
1699 // hostname option.
1700 bool should_send_hostname = static_cast<bool>(opt_hostname);
1701 // Hostname option is not present, so we have to check PRL option.
1702 if (!should_send_hostname) {
1704 option_prl = boost::dynamic_pointer_cast<OptionUint8Array>
1705 (ex.getQuery()->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
1706 if (option_prl) {
1707 // PRL option exists, so check if the hostname option code is
1708 // included in it.
1709 const std::vector<uint8_t>&
1710 requested_opts = option_prl->getValues();
1711 if (std::find(requested_opts.begin(), requested_opts.end(),
1712 DHO_HOST_NAME) != requested_opts.end()) {
1713 // Client has requested hostname via Parameter Request
1714 // List option.
1715 should_send_hostname = true;
1716 }
1717 }
1718 }
1719
1720 // If the hostname or PRL option indicates that the server should
1721 // send back a hostname option, send this option with a reserved
1722 // name for this client.
1723 if (should_send_hostname) {
1724 std::string hostname =
1725 d2_mgr.qualifyName(ctx->currentHost()->getHostname(), false);
1726
1727 // Convert hostname to lower case.
1728 boost::algorithm::to_lower(hostname);
1729
1731 DHCP4_RESERVED_HOSTNAME_ASSIGNED)
1732 .arg(ex.getQuery()->getLabel())
1733 .arg(hostname);
1734 OptionStringPtr opt_hostname_resp(new OptionString(Option::V4,
1736 hostname));
1737 ex.getResponse()->addOption(opt_hostname_resp);
1738
1739 // We're done here.
1740 return;
1741 }
1742 }
1743
1744 // There is no reservation for this client or the client hasn't requested
1745 // hostname option. There is still a possibility that we'll have to send
1746 // hostname option to this client if the client has included hostname option
1747 // but there is no reservation, or the configuration of the server requires
1748 // that we send the option regardless.
1749
1750 D2ClientConfig::ReplaceClientNameMode replace_name_mode =
1751 d2_mgr.getD2ClientConfig()->getReplaceClientNameMode();
1752
1753 // If we don't have a hostname then either we'll supply it or do nothing.
1754 if (!opt_hostname) {
1755 // If we're configured to supply it then add it to the response.
1756 // Use the root domain to signal later on that we should replace it.
1757 if (replace_name_mode == D2ClientConfig::RCM_ALWAYS ||
1758 replace_name_mode == D2ClientConfig::RCM_WHEN_NOT_PRESENT) {
1760 DHCP4_GENERATE_FQDN)
1761 .arg(ex.getQuery()->getLabel());
1762 OptionStringPtr opt_hostname_resp(new OptionString(Option::V4,
1764 "."));
1765 ex.getResponse()->addOption(opt_hostname_resp);
1766 }
1767
1768 return;
1769 }
1770
1771 // Client sent us a hostname option so figure out what to do with it.
1772 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_HOSTNAME_DATA)
1773 .arg(ex.getQuery()->getLabel())
1774 .arg(opt_hostname->getValue());
1775
1776 std::string hostname = isc::util::str::trim(opt_hostname->getValue());
1777 unsigned int label_count = OptionDataTypeUtil::getLabelCount(hostname);
1778 // The hostname option sent by the client should be at least 1 octet long.
1779 // If it isn't we ignore this option. (Per RFC 2131, section 3.14)
1782 if (label_count == 0) {
1783 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_EMPTY_HOSTNAME)
1784 .arg(ex.getQuery()->getLabel());
1785 return;
1786 }
1787
1788 // Stores the value we eventually use, so we can send it back.
1789 OptionStringPtr opt_hostname_resp;
1790
1791 // The hostname option may be unqualified or fully qualified. The lab_count
1792 // holds the number of labels for the name. The number of 1 means that
1793 // there is only root label "." (even for unqualified names, as the
1794 // getLabelCount function treats each name as a fully qualified one).
1795 // By checking the number of labels present in the hostname we may infer
1796 // whether client has sent the fully qualified or unqualified hostname.
1797
1798 if ((replace_name_mode == D2ClientConfig::RCM_ALWAYS ||
1799 replace_name_mode == D2ClientConfig::RCM_WHEN_PRESENT)
1800 || label_count < 2) {
1801 // Set to root domain to signal later on that we should replace it.
1802 // DHO_HOST_NAME is a string option which cannot be empty.
1810 opt_hostname_resp.reset(new OptionString(Option::V4, DHO_HOST_NAME, "."));
1811 } else {
1812 // Sanitize the name the client sent us, if we're configured to do so.
1814 ->getHostnameSanitizer();
1815 if (sanitizer) {
1816 hostname = sanitizer->scrub(hostname);
1817 }
1818
1819 // Convert hostname to lower case.
1820 boost::algorithm::to_lower(hostname);
1821
1822 if (label_count == 2) {
1823 // If there are two labels, it means that the client has specified
1824 // the unqualified name. We have to concatenate the unqualified name
1825 // with the domain name. The false value passed as a second argument
1826 // indicates that the trailing dot should not be appended to the
1827 // hostname. We don't want to append the trailing dot because
1828 // we don't know whether the hostname is partial or not and some
1829 // clients do not handle the hostnames with the trailing dot.
1830 opt_hostname_resp.reset(new OptionString(Option::V4, DHO_HOST_NAME,
1831 d2_mgr.qualifyName(hostname, false)));
1832 } else {
1833 opt_hostname_resp.reset(new OptionString(Option::V4, DHO_HOST_NAME, hostname));
1834 }
1835 }
1836
1837 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_RESPONSE_HOSTNAME_DATA)
1838 .arg(ex.getQuery()->getLabel())
1839 .arg(opt_hostname_resp->getValue());
1840 ex.getResponse()->addOption(opt_hostname_resp);
1841}
1842
1843void
1845 const Lease4Ptr& old_lease) {
1846 if (!lease) {
1848 "NULL lease specified when creating NameChangeRequest");
1849
1850 } else if (!old_lease || !lease->hasIdenticalFqdn(*old_lease)) {
1851 if (old_lease) {
1852 // Queue's up a remove of the old lease's DNS (if needed)
1853 queueNCR(CHG_REMOVE, old_lease);
1854 }
1855
1856 // We may need to generate the NameChangeRequest for the new lease. It
1857 // will be generated only if hostname is set and if forward or reverse
1858 // update has been requested.
1859 queueNCR(CHG_ADD, lease);
1860 }
1861}
1862
1863void
1865 // Get the pointers to the query and the response messages.
1866 Pkt4Ptr query = ex.getQuery();
1867 Pkt4Ptr resp = ex.getResponse();
1868
1869 // Get the context.
1871
1872 // Subnet should have been already selected when the context was created.
1873 Subnet4Ptr subnet = ctx->subnet_;
1874 if (!subnet) {
1875 // This particular client is out of luck today. We do not have
1876 // information about the subnet he is connected to. This likely means
1877 // misconfiguration of the server (or some relays).
1878
1879 // Perhaps this should be logged on some higher level?
1880 LOG_ERROR(bad_packet4_logger, DHCP4_PACKET_NAK_0001)
1881 .arg(query->getLabel())
1882 .arg(query->getRemoteAddr().toText())
1883 .arg(query->getName());
1884 resp->setType(DHCPNAK);
1885 resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
1886 return;
1887 }
1888
1889
1890 // Get the server identifier. It will be used to determine the state
1891 // of the client.
1892 OptionCustomPtr opt_serverid = boost::dynamic_pointer_cast<
1893 OptionCustom>(query->getOption(DHO_DHCP_SERVER_IDENTIFIER));
1894
1895 // Check if the client has sent a requested IP address option or
1896 // ciaddr.
1897 OptionCustomPtr opt_requested_address = boost::dynamic_pointer_cast<
1898 OptionCustom>(query->getOption(DHO_DHCP_REQUESTED_ADDRESS));
1900 if (opt_requested_address) {
1901 hint = opt_requested_address->readAddress();
1902
1903 } else if (!query->getCiaddr().isV4Zero()) {
1904 hint = query->getCiaddr();
1905
1906 }
1907
1908 HWAddrPtr hwaddr = query->getHWAddr();
1909
1910 // "Fake" allocation is processing of DISCOVER message. We pretend to do an
1911 // allocation, but we do not put the lease in the database. That is ok,
1912 // because we do not guarantee that the user will get that exact lease. If
1913 // the user selects this server to do actual allocation (i.e. sends REQUEST)
1914 // it should include this hint. That will help us during the actual lease
1915 // allocation.
1916 bool fake_allocation = (query->getType() == DHCPDISCOVER);
1917
1918 // Get client-id. It is not mandatory in DHCPv4.
1919 ClientIdPtr client_id = ex.getContext()->clientid_;
1920
1921 // If there is no server id and there is a Requested IP Address option
1922 // the client is in the INIT-REBOOT state in which the server has to
1923 // determine whether the client's notion of the address is correct
1924 // and whether the client is known, i.e., has a lease.
1925 if (!fake_allocation && !opt_serverid && opt_requested_address) {
1926
1927 LOG_INFO(lease4_logger, DHCP4_INIT_REBOOT)
1928 .arg(query->getLabel())
1929 .arg(hint.toText());
1930
1931 Lease4Ptr lease;
1932 Subnet4Ptr original_subnet = subnet;
1933
1934 // We used to issue a separate query (two actually: one for client-id
1935 // and another one for hw-addr for) each subnet in the shared network.
1936 // That was horribly inefficient if the client didn't have any lease
1937 // (or there were many subnets and the client happended to be in one
1938 // of the last subnets).
1939 //
1940 // We now issue at most two queries: get all the leases for specific
1941 // client-id and then get all leases for specific hw-address.
1942 if (client_id) {
1943
1944 // Get all the leases for this client-id
1945 Lease4Collection leases_client_id = LeaseMgrFactory::instance().getLease4(*client_id);
1946 if (!leases_client_id.empty()) {
1947 Subnet4Ptr s = original_subnet;
1948
1949 // Among those returned try to find a lease that belongs to
1950 // current shared network.
1951 while (s) {
1952 for (auto l = leases_client_id.begin(); l != leases_client_id.end(); ++l) {
1953 if ((*l)->subnet_id_ == s->getID()) {
1954 lease = *l;
1955 break;
1956 }
1957 }
1958
1959 if (lease) {
1960 break;
1961
1962 } else {
1963 s = s->getNextSubnet(original_subnet, query->getClasses());
1964 }
1965 }
1966 }
1967 }
1968
1969 // If we haven't found a lease yet, try again by hardware-address.
1970 // The logic is the same.
1971 if (!lease && hwaddr) {
1972
1973 // Get all leases for this particular hw-address.
1974 Lease4Collection leases_hwaddr = LeaseMgrFactory::instance().getLease4(*hwaddr);
1975 if (!leases_hwaddr.empty()) {
1976 Subnet4Ptr s = original_subnet;
1977
1978 // Pick one that belongs to a subnet in this shared network.
1979 while (s) {
1980 for (auto l = leases_hwaddr.begin(); l != leases_hwaddr.end(); ++l) {
1981 if ((*l)->subnet_id_ == s->getID()) {
1982 lease = *l;
1983 break;
1984 }
1985 }
1986
1987 if (lease) {
1988 break;
1989
1990 } else {
1991 s = s->getNextSubnet(original_subnet, query->getClasses());
1992 }
1993 }
1994 }
1995 }
1996
1997 // Check the first error case: unknown client. We check this before
1998 // validating the address sent because we don't want to respond if
1999 // we don't know this client, except if we're authoritative.
2000 bool authoritative = original_subnet->getAuthoritative();
2001 bool known_client = lease && lease->belongsToClient(hwaddr, client_id);
2002 if (!authoritative && !known_client) {
2004 DHCP4_NO_LEASE_INIT_REBOOT)
2005 .arg(query->getLabel())
2006 .arg(hint.toText());
2007
2008 ex.deleteResponse();
2009 return;
2010 }
2011
2012 // If we know this client, check if his notion of the IP address is
2013 // correct, if we don't know him, check if we are authoritative.
2014 if ((known_client && (lease->addr_ != hint)) ||
2015 (!known_client && authoritative)) {
2017 DHCP4_PACKET_NAK_0002)
2018 .arg(query->getLabel())
2019 .arg(hint.toText());
2020
2021 resp->setType(DHCPNAK);
2022 resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2023 return;
2024 }
2025 }
2026
2027
2028 CalloutHandlePtr callout_handle = getCalloutHandle(query);
2029
2030 std::string hostname;
2031 bool fqdn_fwd = false;
2032 bool fqdn_rev = false;
2033 OptionStringPtr opt_hostname;
2034 Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
2035 Option4ClientFqdn>(resp->getOption(DHO_FQDN));
2036 if (fqdn) {
2037 hostname = fqdn->getDomainName();
2039 fqdn_fwd,
2040 fqdn_rev);
2041 } else {
2042 opt_hostname = boost::dynamic_pointer_cast<OptionString>
2043 (resp->getOption(DHO_HOST_NAME));
2044
2045 if (opt_hostname) {
2046 hostname = opt_hostname->getValue();
2047 // DHO_HOST_NAME is string option which cannot be blank,
2048 // we use "." to know we should replace it with a fully
2049 // generated name. The local string variable needs to be
2050 // blank in logic below.
2051 if (hostname == ".") {
2052 hostname = "";
2053 }
2056 fqdn_fwd = true;
2057 fqdn_rev = true;
2058 }
2059 }
2060
2061 // We need to set these values in the context as they haven't been set yet.
2062 ctx->requested_address_ = hint;
2063 ctx->fwd_dns_update_ = fqdn_fwd;
2064 ctx->rev_dns_update_ = fqdn_rev;
2065 ctx->hostname_ = hostname;
2066 ctx->fake_allocation_ = fake_allocation;
2067 ctx->callout_handle_ = callout_handle;
2068
2069 Lease4Ptr lease = alloc_engine_->allocateLease4(*ctx);
2070
2071 // Subnet may be modified by the allocation engine, if the initial subnet
2072 // belongs to a shared network.
2073 if (subnet->getID() != ctx->subnet_->getID()) {
2074 SharedNetwork4Ptr network;
2075 subnet->getSharedNetwork(network);
2076 if (network) {
2077 LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_SUBNET_DYNAMICALLY_CHANGED)
2078 .arg(query->getLabel())
2079 .arg(subnet->toText())
2080 .arg(ctx->subnet_->toText())
2081 .arg(network->getName());
2082 }
2083 subnet = ctx->subnet_;
2084 }
2085
2086 if (lease) {
2087 // We have a lease! Let's set it in the packet and send it back to
2088 // the client.
2089 LOG_INFO(lease4_logger, fake_allocation ? DHCP4_LEASE_ADVERT : DHCP4_LEASE_ALLOC)
2090 .arg(query->getLabel())
2091 .arg(lease->addr_.toText());
2092
2093 // We're logging this here, because this is the place where we know
2094 // which subnet has been actually used for allocation. If the
2095 // client identifier matching is disabled, we want to make sure that
2096 // the user is notified.
2097 if (!ctx->subnet_->getMatchClientId()) {
2098 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENTID_IGNORED_FOR_LEASES)
2099 .arg(ctx->query_->getLabel())
2100 .arg(ctx->subnet_->getID());
2101 }
2102
2103 resp->setYiaddr(lease->addr_);
2104
2109 if (!fake_allocation) {
2110 // If this is a renewing client it will set a ciaddr which the
2111 // server may include in the response. If this is a new allocation
2112 // the client will set ciaddr to 0 and this will also be propagated
2113 // to the server's resp.
2114 resp->setCiaddr(query->getCiaddr());
2115 }
2116
2117 // We may need to update FQDN or hostname if the server is to generate
2118 // new name from the allocated IP address or if the allocation engine
2119 // has switched to a different subnet (from the same shared network)
2120 // where the client has hostname reservations.
2121 if (fqdn || opt_hostname) {
2122 bool should_update = false;
2123
2124 // If there is a reservation in the current subnet for a hostname,
2125 // we need to use this reserved name.
2126 if (ctx->currentHost() && !ctx->currentHost()->getHostname().empty()) {
2127
2128 lease->hostname_ = CfgMgr::instance().getD2ClientMgr()
2129 .qualifyName(ctx->currentHost()->getHostname(),
2130 static_cast<bool>(fqdn));
2131 should_update = true;
2132
2133 // If there has been Client FQDN or Hostname option sent, but the
2134 // hostname is empty, it means that server is responsible for
2135 // generating the entire hostname for the client. The example of the
2136 // client's name, generated from the IP address is: host-192-0-2-3.
2137 } else if (lease->hostname_.empty()) {
2138
2139 // Note that if we have received the hostname option, rather than
2140 // Client FQDN the trailing dot is not appended to the generated
2141 // hostname because some clients don't handle the trailing dot in
2142 // the hostname. Whether the trailing dot is appended or not is
2143 // controlled by the second argument to the generateFqdn().
2144 lease->hostname_ = CfgMgr::instance().getD2ClientMgr()
2145 .generateFqdn(lease->addr_, static_cast<bool>(fqdn));
2146
2147 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_RESPONSE_HOSTNAME_GENERATE)
2148 .arg(query->getLabel())
2149 .arg(lease->hostname_);
2150
2151 should_update = true;
2152 }
2153
2154 if (should_update) {
2155
2156 // The operations below are rather safe, but we want to catch
2157 // any potential exceptions (e.g. invalid lease database backend
2158 // implementation) and log an error.
2159 try {
2160 if (!fake_allocation) {
2161 // The lease update should be safe, because the lease should
2162 // be already in the database. In most cases the exception
2163 // would be thrown if the lease was missing.
2165 }
2166
2167 // The name update in the option should be also safe,
2168 // because the generated name is well formed.
2169 if (fqdn) {
2170 fqdn->setDomainName(lease->hostname_,
2172 } else if (opt_hostname) {
2173 opt_hostname->setValue(lease->hostname_);
2174 }
2175
2176 } catch (const Exception& ex) {
2177 LOG_ERROR(ddns4_logger, DHCP4_POST_ALLOCATION_NAME_UPDATE_FAIL)
2178 .arg(query->getLabel())
2179 .arg(lease->hostname_)
2180 .arg(ex.what());
2181 }
2182 }
2183 }
2184
2185 // IP Address Lease time (type 51)
2187 lease->valid_lft_));
2188 resp->addOption(opt);
2189
2190 // Subnet mask (type 1)
2191 resp->addOption(getNetmaskOption(subnet));
2192
2193 // rebind timer (type 59) - if specified then send it only it if
2194 // it is less than lease lifetime. Note we "sanity" check T1
2195 // and T2 against lease lifetime here in event the lifetime has
2196 // been altered somewhere along the line.
2197 uint32_t timer_ceiling = lease->valid_lft_;
2198 if ((!subnet->getT2().unspecified()) &&
2199 (subnet->getT2() < timer_ceiling)) {
2202 subnet->getT2()));
2203 resp->addOption(t2);
2204
2205 // If T2 is specified, then it becomes the ceiling for T1
2206 timer_ceiling = subnet->getT2();
2207 }
2208
2209 // renewal-timer (type 58) - if specified then send it only if
2210 // it is less than the ceiling (T2 if given, lease life time if not)
2211 if ((!subnet->getT1().unspecified()) &&
2212 (subnet->getT1() < timer_ceiling)) {
2215 subnet->getT1()));
2216 resp->addOption(t1);
2217 }
2218
2219
2220 // Create NameChangeRequests if DDNS is enabled and this is a
2221 // real allocation.
2222 if (!fake_allocation && CfgMgr::instance().ddnsEnabled()) {
2223 try {
2224 LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_NCR_CREATE)
2225 .arg(query->getLabel());
2226 createNameChangeRequests(lease, ctx->old_lease_);
2227
2228 } catch (const Exception& ex) {
2229 LOG_ERROR(ddns4_logger, DHCP4_NCR_CREATION_FAILED)
2230 .arg(query->getLabel())
2231 .arg(ex.what());
2232 }
2233 }
2234
2235 } else {
2236 // Allocation engine did not allocate a lease. The engine logged
2237 // cause of that failure.
2239 DHCP4_PACKET_NAK_0003 : DHCP4_PACKET_NAK_0004)
2240 .arg(query->getLabel())
2241 .arg(query->getCiaddr().toText())
2242 .arg(opt_requested_address ?
2243 opt_requested_address->readAddress().toText() : "(no address)");
2244
2245 resp->setType(DHCPNAK);
2246 resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2247
2248 resp->delOption(DHO_FQDN);
2249 resp->delOption(DHO_HOST_NAME);
2250 }
2251}
2252
2253uint16_t
2255
2256 // Look for a relay-port RAI sub-option in the query.
2257 const Pkt4Ptr& query = ex.getQuery();
2258 const OptionPtr& rai = query->getOption(DHO_DHCP_AGENT_OPTIONS);
2259 if (rai && rai->getOption(RAI_OPTION_RELAY_PORT)) {
2260 // Got the sub-option so use the remote port set by the relay.
2261 return (query->getRemotePort());
2262 }
2263 return (0);
2264}
2265
2266void
2268 adjustRemoteAddr(ex);
2269
2270 // Initialize the pointers to the client's message and the server's
2271 // response.
2272 Pkt4Ptr query = ex.getQuery();
2273 Pkt4Ptr response = ex.getResponse();
2274
2275 // The DHCPINFORM is generally unicast to the client. The only situation
2276 // when the server is unable to unicast to the client is when the client
2277 // doesn't include ciaddr and the message is relayed. In this case the
2278 // server has to reply via relay agent. For other messages we send back
2279 // through relay if message is relayed, and unicast to the client if the
2280 // message is not relayed.
2281 // Note that the call to this function may throw if invalid combination
2282 // of hops and giaddr is found (hops = 0 if giaddr = 0 and hops != 0 if
2283 // giaddr != 0). The exception will propagate down and eventually cause the
2284 // packet to be discarded.
2285 if (((query->getType() == DHCPINFORM) &&
2286 ((!query->getCiaddr().isV4Zero()) ||
2287 (!query->isRelayed() && !query->getRemoteAddr().isV4Zero()))) ||
2288 ((query->getType() != DHCPINFORM) && !query->isRelayed())) {
2289 response->setRemotePort(DHCP4_CLIENT_PORT);
2290
2291 } else {
2292 // RFC 8357 section 5.1
2293 uint16_t relay_port = checkRelayPort(ex);
2294 response->setRemotePort(relay_port ? relay_port : DHCP4_SERVER_PORT);
2295 }
2296
2297 CfgIfacePtr cfg_iface = CfgMgr::instance().getCurrentCfg()->getCfgIface();
2298 if (query->isRelayed() &&
2299 (cfg_iface->getSocketType() == CfgIface::SOCKET_UDP) &&
2300 (cfg_iface->getOutboundIface() == CfgIface::USE_ROUTING)) {
2301
2302 // Mark the response to follow routing
2303 response->setLocalAddr(IOAddress::IPV4_ZERO_ADDRESS());
2304 response->resetIndex();
2305 // But keep the interface name
2306 response->setIface(query->getIface());
2307
2308 } else {
2309
2310 IOAddress local_addr = query->getLocalAddr();
2311
2312 // In many cases the query is sent to a broadcast address. This address
2313 // appears as a local address in the query message. We can't simply copy
2314 // this address to a response message and use it as a source address.
2315 // Instead we will need to use the address assigned to the interface
2316 // on which the query has been received. In other cases, we will just
2317 // use this address as a source address for the response.
2318 // Do the same for DHCPv4-over-DHCPv6 exchanges.
2319 if (local_addr.isV4Bcast() || query->isDhcp4o6()) {
2320 SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
2321 local_addr = sock_info.addr_;
2322 }
2323
2324 // We assume that there is an appropriate socket bound to this address
2325 // and that the address is correct. This is safe assumption because
2326 // the local address of the query is set when the query is received.
2327 // The query sent to an incorrect address wouldn't have been received.
2328 // However, if socket is closed for this address between the reception
2329 // of the query and sending a response, the IfaceMgr should detect it
2330 // and return an error.
2331 response->setLocalAddr(local_addr);
2332 // In many cases the query is sent to a broadcast address. This address
2333 // appears as a local address in the query message. Therefore we can't
2334 // simply copy local address from the query and use it as a source
2335 // address for the response. Instead, we have to check what address our
2336 // socket is bound to and use it as a source address. This operation
2337 // may throw if for some reason the socket is closed.
2340 response->setIndex(query->getIndex());
2341 response->setIface(query->getIface());
2342 }
2343
2344 response->setLocalPort(DHCP4_SERVER_PORT);
2345}
2346
2347void
2349 // Initialize the pointers to the client's message and the server's
2350 // response.
2351 Pkt4Ptr query = ex.getQuery();
2352 Pkt4Ptr response = ex.getResponse();
2353
2354 // DHCPv4-over-DHCPv6 is simple
2355 if (query->isDhcp4o6()) {
2356 response->setRemoteAddr(query->getRemoteAddr());
2357 return;
2358 }
2359
2360 // The DHCPINFORM is slightly different than other messages in a sense
2361 // that the server should always unicast the response to the ciaddr.
2362 // It appears however that some clients don't set the ciaddr. We still
2363 // want to provision these clients and we do what we can't to send the
2364 // packet to the address where client can receive it.
2365 if (query->getType() == DHCPINFORM) {
2366 // If client adheres to RFC2131 it will set the ciaddr and in this
2367 // case we always unicast our response to this address.
2368 if (!query->getCiaddr().isV4Zero()) {
2369 response->setRemoteAddr(query->getCiaddr());
2370
2371 // If we received DHCPINFORM via relay and the ciaddr is not set we
2372 // will try to send the response via relay. The caveat is that the
2373 // relay will not have any idea where to forward the packet because
2374 // the yiaddr is likely not set. So, the broadcast flag is set so
2375 // as the response may be broadcast.
2376 } else if (query->isRelayed()) {
2377 response->setRemoteAddr(query->getGiaddr());
2378 response->setFlags(response->getFlags() | BOOTP_BROADCAST);
2379
2380 // If there is no ciaddr and no giaddr the only thing we can do is
2381 // to use the source address of the packet.
2382 } else {
2383 response->setRemoteAddr(query->getRemoteAddr());
2384 }
2385 // Remote address is now set so return.
2386 return;
2387 }
2388
2389 // If received relayed message, server responds to the relay address.
2390 if (query->isRelayed()) {
2391 // The client should set the ciaddr when sending the DHCPINFORM
2392 // but in case he didn't, the relay may not be able to determine the
2393 // address of the client, because yiaddr is not set when responding
2394 // to Confirm and the only address available was the source address
2395 // of the client. The source address is however not used here because
2396 // the message is relayed. Therefore, we set the BROADCAST flag so
2397 // as the relay can broadcast the packet.
2398 if ((query->getType() == DHCPINFORM) &&
2399 query->getCiaddr().isV4Zero()) {
2400 response->setFlags(BOOTP_BROADCAST);
2401 }
2402 response->setRemoteAddr(query->getGiaddr());
2403
2404 // If giaddr is 0 but client set ciaddr, server should unicast the
2405 // response to ciaddr.
2406 } else if (!query->getCiaddr().isV4Zero()) {
2407 response->setRemoteAddr(query->getCiaddr());
2408
2409 // We can't unicast the response to the client when sending NAK,
2410 // because we haven't allocated address for him. Therefore,
2411 // NAK is broadcast.
2412 } else if (response->getType() == DHCPNAK) {
2413 response->setRemoteAddr(IOAddress::IPV4_BCAST_ADDRESS());
2414
2415 // If yiaddr is set it means that we have created a lease for a client.
2416 } else if (!response->getYiaddr().isV4Zero()) {
2417 // If the broadcast bit is set in the flags field, we have to
2418 // send the response to broadcast address. Client may have requested it
2419 // because it doesn't support reception of messages on the interface
2420 // which doesn't have an address assigned. The other case when response
2421 // must be broadcasted is when our server does not support responding
2422 // directly to a client without address assigned.
2423 const bool bcast_flag = ((query->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
2424 if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
2425 response->setRemoteAddr(IOAddress::IPV4_BCAST_ADDRESS());
2426
2427 // Client cleared the broadcast bit and we support direct responses
2428 // so we should unicast the response to a newly allocated address -
2429 // yiaddr.
2430 } else {
2431 response->setRemoteAddr(response ->getYiaddr());
2432
2433 }
2434
2435 // In most cases, we should have the remote address found already. If we
2436 // found ourselves at this point, the rational thing to do is to respond
2437 // to the address we got the query from.
2438 } else {
2439 response->setRemoteAddr(query->getRemoteAddr());
2440
2441 }
2442}
2443
2444void
2446 Pkt4Ptr query = ex.getQuery();
2447 Pkt4Ptr response = ex.getResponse();
2448
2449 // Step 1: Start with fixed fields defined on subnet level.
2450 Subnet4Ptr subnet = ex.getContext()->subnet_;
2451 if (subnet) {
2452 IOAddress subnet_next_server = subnet->getSiaddr();
2453 if (!subnet_next_server.isV4Zero()) {
2454 response->setSiaddr(subnet_next_server);
2455 }
2456
2457 const string& sname = subnet->getSname();
2458 if (!sname.empty()) {
2459 // Converting string to (const uint8_t*, size_t len) format is
2460 // tricky. reinterpret_cast is not the most elegant solution,
2461 // but it does avoid us making unnecessary copy. We will convert
2462 // sname and file fields in Pkt4 to string one day and life
2463 // will be easier.
2464 response->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
2465 sname.size());
2466 }
2467
2468 const string& filename = subnet->getFilename();
2469 if (!filename.empty()) {
2470 // Converting string to (const uint8_t*, size_t len) format is
2471 // tricky. reinterpret_cast is not the most elegant solution,
2472 // but it does avoid us making unnecessary copy. We will convert
2473 // sname and file fields in Pkt4 to string one day and life
2474 // will be easier.
2475 response->setFile(reinterpret_cast<const uint8_t*>(filename.c_str()),
2476 filename.size());
2477 }
2478 }
2479
2480 // Step 2: Try to set the values based on classes.
2481 // Any values defined in classes will override those from subnet level.
2482 const ClientClasses classes = query->getClasses();
2483 if (!classes.empty()) {
2484
2485 // Let's get class definitions
2486 const ClientClassDictionaryPtr& dict =
2487 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
2488
2489 // Now we need to iterate over the classes assigned to the
2490 // query packet and find corresponding class definitions for it.
2491 for (ClientClasses::const_iterator name = classes.cbegin();
2492 name != classes.cend(); ++name) {
2493
2494 ClientClassDefPtr cl = dict->findClass(*name);
2495 if (!cl) {
2496 // Let's skip classes that don't have definitions. Currently
2497 // these are automatic classes VENDOR_CLASS_something, but there
2498 // may be other classes assigned under other circumstances, e.g.
2499 // by hooks.
2500 continue;
2501 }
2502
2503 IOAddress next_server = cl->getNextServer();
2504 if (!next_server.isV4Zero()) {
2505 response->setSiaddr(next_server);
2506 }
2507
2508 const string& sname = cl->getSname();
2509 if (!sname.empty()) {
2510 // Converting string to (const uint8_t*, size_t len) format is
2511 // tricky. reinterpret_cast is not the most elegant solution,
2512 // but it does avoid us making unnecessary copy. We will convert
2513 // sname and file fields in Pkt4 to string one day and life
2514 // will be easier.
2515 response->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
2516 sname.size());
2517 }
2518
2519 const string& filename = cl->getFilename();
2520 if (!filename.empty()) {
2521 // Converting string to (const uint8_t*, size_t len) format is
2522 // tricky. reinterpret_cast is not the most elegant solution,
2523 // but it does avoid us making unnecessary copy. We will convert
2524 // sname and file fields in Pkt4 to string one day and life
2525 // will be easier.
2526 response->setFile(reinterpret_cast<const uint8_t*>(filename.c_str()),
2527 filename.size());
2528 }
2529 }
2530 }
2531
2532 // Step 3: try to set values using HR. Any values coming from there will override
2533 // the subnet or class values.
2535}
2536
2538Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
2539 uint32_t netmask = getNetmask4(subnet->get().second).toUint32();
2540
2542 DHO_SUBNET_MASK, netmask));
2543
2544 return (opt);
2545}
2546
2547Pkt4Ptr
2549 sanityCheck(discover, FORBIDDEN);
2550
2551 bool drop = false;
2552 Dhcpv4Exchange ex(alloc_engine_, discover, selectSubnet(discover, drop));
2553
2554 // Stop here if selectSubnet decided to drop the packet
2555 if (drop) {
2556 return (Pkt4Ptr());
2557 }
2558
2559 // If DHCPDISCOVER message contains the FQDN or Hostname option, server
2560 // may respond to the client with the appropriate FQDN or Hostname
2561 // option to indicate that whether it will take responsibility for
2562 // updating DNS when the client sends DHCPREQUEST message.
2564
2565 assignLease(ex);
2566
2567 if (!ex.getResponse()) {
2568 // The offer is empty so return it *now*!
2569 return (Pkt4Ptr());
2570 }
2571
2572 // Adding any other options makes sense only when we got the lease.
2573 if (!ex.getResponse()->getYiaddr().isV4Zero()) {
2574 // Assign reserved classes.
2576 // Required classification
2577 requiredClassify(ex);
2578
2582 // There are a few basic options that we always want to
2583 // include in the response. If client did not request
2584 // them we append them for him.
2586
2587 // Set fixed fields (siaddr, sname, filename) if defined in
2588 // the reservation, class or subnet specific configuration.
2589 setFixedFields(ex);
2590
2591 } else {
2592 // If the server can't offer an address, it drops the packet.
2593 return (Pkt4Ptr());
2594
2595 }
2596
2597 // Set the src/dest IP address, port and interface for the outgoing
2598 // packet.
2599 adjustIfaceData(ex);
2600
2601 appendServerID(ex);
2602
2603 return (ex.getResponse());
2604}
2605
2606Pkt4Ptr
2610
2611 bool drop = false;
2612 Dhcpv4Exchange ex(alloc_engine_, request, selectSubnet(request, drop));
2613
2614 // Stop here if selectSubnet decided to drop the packet
2615 if (drop) {
2616 return (Pkt4Ptr());
2617 }
2618
2619 // If DHCPREQUEST message contains the FQDN or Hostname option, server
2620 // should respond to the client with the appropriate FQDN or Hostname
2621 // option to indicate if it takes responsibility for the DNS updates.
2622 // This is performed by the function below.
2624
2625 // Note that we treat REQUEST message uniformly, regardless if this is a
2626 // first request (requesting for new address), renewing existing address
2627 // or even rebinding.
2628 assignLease(ex);
2629
2630 if (!ex.getResponse()) {
2631 // The ack is empty so return it *now*!
2632 return (Pkt4Ptr());
2633 }
2634
2635 // Adding any other options makes sense only when we got the lease.
2636 if (!ex.getResponse()->getYiaddr().isV4Zero()) {
2637 // Assign reserved classes.
2639 // Required classification
2640 requiredClassify(ex);
2641
2645 // There are a few basic options that we always want to
2646 // include in the response. If client did not request
2647 // them we append them for him.
2649
2650 // Set fixed fields (siaddr, sname, filename) if defined in
2651 // the reservation, class or subnet specific configuration.
2652 setFixedFields(ex);
2653 }
2654
2655 // Set the src/dest IP address, port and interface for the outgoing
2656 // packet.
2657 adjustIfaceData(ex);
2658
2659 appendServerID(ex);
2660
2661 // Return the pointer to the context, which will be required by the
2662 // leases4_comitted callouts.
2663 context = ex.getContext();
2664
2665 return (ex.getResponse());
2666}
2667
2668void
2672
2673 // Try to find client-id. Note that for the DHCPRELEASE we don't check if the
2674 // match-client-id configuration parameter is disabled because this parameter
2675 // is configured for subnets and we don't select subnet for the DHCPRELEASE.
2676 // Bogus clients usually generate new client identifiers when they first
2677 // connect to the network, so whatever client identifier has been used to
2678 // acquire the lease, the client identifier carried in the DHCPRELEASE is
2679 // likely to be the same and the lease will be correctly identified in the
2680 // lease database. If supplied client identifier differs from the one used
2681 // to acquire the lease then the lease will remain in the database and
2682 // simply expire.
2683 ClientIdPtr client_id;
2684 OptionPtr opt = release->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
2685 if (opt) {
2686 client_id = ClientIdPtr(new ClientId(opt->getData()));
2687 }
2688
2689 try {
2690 // Do we have a lease for that particular address?
2691 Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getCiaddr());
2692
2693 if (!lease) {
2694 // No such lease - bogus release
2695 LOG_DEBUG(lease4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_NO_LEASE)
2696 .arg(release->getLabel())
2697 .arg(release->getCiaddr().toText());
2698 return;
2699 }
2700
2701 if (!lease->belongsToClient(release->getHWAddr(), client_id)) {
2702 LOG_DEBUG(lease4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_CLIENT)
2703 .arg(release->getLabel())
2704 .arg(release->getCiaddr().toText());
2705 return;
2706 }
2707
2708 bool skip = false;
2709
2710 // Execute all callouts registered for lease4_release
2711 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_release_)) {
2712 CalloutHandlePtr callout_handle = getCalloutHandle(release);
2713
2714 // Use the RAII wrapper to make sure that the callout handle state is
2715 // reset when this object goes out of scope. All hook points must do
2716 // it to prevent possible circular dependency between the callout
2717 // handle and its arguments.
2718 ScopedCalloutHandleState callout_handle_state(callout_handle);
2719
2720 // Enable copying options from the packet within hook library.
2721 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(release);
2722
2723 // Pass the original packet
2724 callout_handle->setArgument("query4", release);
2725
2726 // Pass the lease to be updated
2727 callout_handle->setArgument("lease4", lease);
2728
2729 // Call all installed callouts
2730 HooksManager::callCallouts(Hooks.hook_index_lease4_release_,
2731 *callout_handle);
2732
2733 // Callouts decided to skip the next processing step. The next
2734 // processing step would to send the packet, so skip at this
2735 // stage means "drop response".
2736 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
2737 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
2738 skip = true;
2740 DHCP4_HOOK_LEASE4_RELEASE_SKIP)
2741 .arg(release->getLabel());
2742 }
2743 }
2744
2745 // Callout didn't indicate to skip the release process. Let's release
2746 // the lease.
2747 if (!skip) {
2748 bool success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
2749
2750 if (success) {
2751
2752 context.reset(new AllocEngine::ClientContext4());
2753 context->old_lease_ = lease;
2754
2755 // Release successful
2756 LOG_INFO(lease4_logger, DHCP4_RELEASE)
2757 .arg(release->getLabel())
2758 .arg(lease->addr_.toText());
2759
2760 // Need to decrease statistic for assigned addresses.
2762 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-addresses"),
2763 static_cast<int64_t>(-1));
2764
2765 // Remove existing DNS entries for the lease, if any.
2766 queueNCR(CHG_REMOVE, lease);
2767
2768 } else {
2769 // Release failed
2770 LOG_ERROR(lease4_logger, DHCP4_RELEASE_FAIL)
2771 .arg(release->getLabel())
2772 .arg(lease->addr_.toText());
2773 }
2774 }
2775 } catch (const isc::Exception& ex) {
2776 LOG_ERROR(lease4_logger, DHCP4_RELEASE_EXCEPTION)
2777 .arg(release->getLabel())
2778 .arg(release->getCiaddr())
2779 .arg(ex.what());
2780 }
2781}
2782
2783void
2785
2786 // Server-id is mandatory in DHCPDECLINE (see table 5, RFC2131)
2788 // sanityCheck(decline, MANDATORY);
2789
2790 // Client is supposed to specify the address being declined in
2791 // Requested IP address option, but must not set its ciaddr.
2792 // (again, see table 5 in RFC2131).
2793
2794 OptionCustomPtr opt_requested_address = boost::dynamic_pointer_cast<
2795 OptionCustom>(decline->getOption(DHO_DHCP_REQUESTED_ADDRESS));
2796 if (!opt_requested_address) {
2797
2798 isc_throw(RFCViolation, "Mandatory 'Requested IP address' option missing"
2799 "in DHCPDECLINE sent from " << decline->getLabel());
2800 }
2801 IOAddress addr(opt_requested_address->readAddress());
2802
2803 // We could also extract client's address from ciaddr, but that's clearly
2804 // against RFC2131.
2805
2806 // Now we need to check whether this address really belongs to the client
2807 // that attempts to decline it.
2808 const Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(addr);
2809
2810 if (!lease) {
2811 // Client tried to decline an address, but we don't have a lease for
2812 // that address. Let's ignore it.
2813 //
2814 // We could assume that we're recovering from a mishandled migration
2815 // to a new server and mark the address as declined, but the window of
2816 // opportunity for that to be useful is small and the attack vector
2817 // would be pretty severe.
2818 LOG_WARN(dhcp4_logger, DHCP4_DECLINE_LEASE_NOT_FOUND)
2819 .arg(addr.toText()).arg(decline->getLabel());
2820 return;
2821 }
2822
2823 // Get client-id, if available.
2824 OptionPtr opt_clientid = decline->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
2825 ClientIdPtr client_id;
2826 if (opt_clientid) {
2827 client_id.reset(new ClientId(opt_clientid->getData()));
2828 }
2829
2830 // Check if the client attempted to decline a lease it doesn't own.
2831 if (!lease->belongsToClient(decline->getHWAddr(), client_id)) {
2832
2833 // Get printable hardware addresses
2834 string client_hw = decline->getHWAddr() ?
2835 decline->getHWAddr()->toText(false) : "(none)";
2836 string lease_hw = lease->hwaddr_ ?
2837 lease->hwaddr_->toText(false) : "(none)";
2838
2839 // Get printable client-ids
2840 string client_id_txt = client_id ? client_id->toText() : "(none)";
2841 string lease_id_txt = lease->client_id_ ?
2842 lease->client_id_->toText() : "(none)";
2843
2844 // Print the warning and we're done here.
2845 LOG_WARN(dhcp4_logger, DHCP4_DECLINE_LEASE_MISMATCH)
2846 .arg(addr.toText()).arg(decline->getLabel())
2847 .arg(client_hw).arg(lease_hw).arg(client_id_txt).arg(lease_id_txt);
2848
2849 return;
2850 }
2851
2852 // Ok, all is good. The client is reporting its own address. Let's
2853 // process it.
2854 declineLease(lease, decline, context);
2855}
2856
2857void
2858Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline,
2860
2861 // Let's check if there are hooks installed for decline4 hook point.
2862 // If they are, let's pass the lease and client's packet. If the hook
2863 // sets status to drop, we reject this Decline.
2864 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_decline_)) {
2865 CalloutHandlePtr callout_handle = getCalloutHandle(decline);
2866
2867 // Use the RAII wrapper to make sure that the callout handle state is
2868 // reset when this object goes out of scope. All hook points must do
2869 // it to prevent possible circular dependency between the callout
2870 // handle and its arguments.
2871 ScopedCalloutHandleState callout_handle_state(callout_handle);
2872
2873 // Enable copying options from the packet within hook library.
2874 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(decline);
2875
2876 // Pass incoming Decline and the lease to be declined.
2877 callout_handle->setArgument("lease4", lease);
2878 callout_handle->setArgument("query4", decline);
2879
2880 // Call callouts
2881 HooksManager::callCallouts(Hooks.hook_index_lease4_decline_,
2882 *callout_handle);
2883
2884 // Check if callouts decided to skip the next processing step.
2885 // If any of them did, we will drop the packet.
2886 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
2887 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
2888 LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_DECLINE_SKIP)
2889 .arg(decline->getLabel()).arg(lease->addr_.toText());
2890 return;
2891 }
2892 }
2893
2894 // Remove existing DNS entries for the lease, if any.
2895 // queueNCR will do the necessary checks and will skip the update, if not needed.
2896 queueNCR(CHG_REMOVE, lease);
2897
2898 // Bump up the statistics.
2899
2900 // Per subnet declined addresses counter.
2902 StatsMgr::generateName("subnet", lease->subnet_id_, "declined-addresses"),
2903 static_cast<int64_t>(1));
2904
2905 // Global declined addresses counter.
2906 StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
2907
2908 // We do not want to decrease the assigned-addresses at this time. While
2909 // technically a declined address is no longer allocated, the primary usage
2910 // of the assigned-addresses statistic is to monitor pool utilization. Most
2911 // people would forget to include declined-addresses in the calculation,
2912 // and simply do assigned-addresses/total-addresses. This would have a bias
2913 // towards under-representing pool utilization, if we decreased allocated
2914 // immediately after receiving DHCPDECLINE, rather than later when we recover
2915 // the address.
2916
2917 // @todo: Call hooks.
2918
2919 // We need to disassociate the lease from the client. Once we move a lease
2920 // to declined state, it is no longer associated with the client in any
2921 // way.
2922 lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
2923
2925
2926 context.reset(new AllocEngine::ClientContext4());
2927 context->new_lease_ = lease;
2928
2929 LOG_INFO(lease4_logger, DHCP4_DECLINE_LEASE).arg(lease->addr_.toText())
2930 .arg(decline->getLabel()).arg(lease->valid_lft_);
2931}
2932
2933Pkt4Ptr
2935 // DHCPINFORM MUST not include server identifier.
2936 sanityCheck(inform, FORBIDDEN);
2937
2938 bool drop = false;
2939 Dhcpv4Exchange ex(alloc_engine_, inform, selectSubnet(inform, drop));
2940
2941 // Stop here if selectSubnet decided to drop the packet
2942 if (drop) {
2943 return (Pkt4Ptr());
2944 }
2945
2946 Pkt4Ptr ack = ex.getResponse();
2947
2949 requiredClassify(ex);
2950
2955 adjustIfaceData(ex);
2956
2957 // Set fixed fields (siaddr, sname, filename) if defined in
2958 // the reservation, class or subnet specific configuration.
2959 setFixedFields(ex);
2960
2961 // There are cases for the DHCPINFORM that the server receives it via
2962 // relay but will send the response to the client's unicast address
2963 // carried in the ciaddr. In this case, the giaddr and hops field should
2964 // be cleared (these fields were copied by the copyDefaultFields function).
2965 // Also Relay Agent Options should be removed if present.
2966 if (ack->getRemoteAddr() != inform->getGiaddr()) {
2967 LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_INFORM_DIRECT_REPLY)
2968 .arg(inform->getLabel())
2969 .arg(ack->getRemoteAddr())
2970 .arg(ack->getIface());
2971 ack->setHops(0);
2972 ack->setGiaddr(IOAddress::IPV4_ZERO_ADDRESS());
2973 ack->delOption(DHO_DHCP_AGENT_OPTIONS);
2974 }
2975
2976 // The DHCPACK must contain server id.
2977 appendServerID(ex);
2978
2979 return (ex.getResponse());
2980}
2981
2982bool
2983Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
2984 // Check that the message type is accepted by the server. We rely on the
2985 // function called to log a message if needed.
2986 if (!acceptMessageType(query)) {
2987 return (false);
2988 }
2989 // Check if the message from directly connected client (if directly
2990 // connected) should be dropped or processed.
2991 if (!acceptDirectRequest(query)) {
2992 LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0002)
2993 .arg(query->getLabel())
2994 .arg(query->getIface());
2995 return (false);
2996 }
2997
2998 // Check if the DHCPv4 packet has been sent to us or to someone else.
2999 // If it hasn't been sent to us, drop it!
3000 if (!acceptServerId(query)) {
3001 LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0003)
3002 .arg(query->getLabel())
3003 .arg(query->getIface());
3004 return (false);
3005 }
3006
3007 return (true);
3008}
3009
3010bool
3012 // Accept all relayed messages.
3013 if (pkt->isRelayed()) {
3014 return (true);
3015 }
3016
3017 // Accept all DHCPv4-over-DHCPv6 messages.
3018 if (pkt->isDhcp4o6()) {
3019 return (true);
3020 }
3021
3022 // The source address must not be zero for the DHCPINFORM message from
3023 // the directly connected client because the server will not know where
3024 // to respond if the ciaddr was not present.
3025 try {
3026 if (pkt->getType() == DHCPINFORM) {
3027 if (pkt->getRemoteAddr().isV4Zero() &&
3028 pkt->getCiaddr().isV4Zero()) {
3029 return (false);
3030 }
3031 }
3032 } catch (...) {
3033 // If we got here, it is probably because the message type hasn't
3034 // been set. But, this should not really happen assuming that
3035 // we validate the message type prior to calling this function.
3036 return (false);
3037 }
3038 bool drop = false;
3039 bool result = (!pkt->getLocalAddr().isV4Bcast() ||
3040 selectSubnet(pkt, drop, true));
3041 if (drop) {
3042 // The packet must be dropped but as sanity_only is true it is dead code.
3043 return (false);
3044 }
3045 return (result);
3046}
3047
3048bool
3050 // When receiving a packet without message type option, getType() will
3051 // throw.
3052 int type;
3053 try {
3054 type = query->getType();
3055
3056 } catch (...) {
3057 LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0004)
3058 .arg(query->getLabel())
3059 .arg(query->getIface());
3060 return (false);
3061 }
3062
3063 // Once we know that the message type is within a range of defined DHCPv4
3064 // messages, we do a detailed check to make sure that the received message
3065 // is targeted at server. Note that we could have received some Offer
3066 // message broadcasted by the other server to a relay. Even though, the
3067 // server would rather unicast its response to a relay, let's be on the
3068 // safe side. Also, we want to drop other messages which we don't support.
3069 // All these valid messages that we are not going to process are dropped
3070 // silently.
3071
3072 switch(type) {
3073 case DHCPDISCOVER:
3074 case DHCPREQUEST:
3075 case DHCPRELEASE:
3076 case DHCPDECLINE:
3077 case DHCPINFORM:
3078 return (true);
3079 break;
3080
3081 case DHCP_NOTYPE:
3082 LOG_INFO(bad_packet4_logger, DHCP4_PACKET_DROP_0009)
3083 .arg(query->getLabel());
3084 break;
3085
3086 default:
3087 // If we receive a message with a non-existing type, we are logging it.
3088 if (type >= DHCP_TYPES_EOF) {
3089 LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0005)
3090 .arg(query->getLabel())
3091 .arg(type);
3092 } else {
3093 // Exists but we don't support it.
3094 LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_0006)
3095 .arg(query->getLabel())
3096 .arg(type);
3097 }
3098 break;
3099 }
3100
3101 return (false);
3102}
3103
3104bool
3106 // This function is meant to be called internally by the server class, so
3107 // we rely on the caller to sanity check the pointer and we don't check
3108 // it here.
3109
3110 // Check if server identifier option is present. If it is not present
3111 // we accept the message because it is targeted to all servers.
3112 // Note that we don't check cases that server identifier is mandatory
3113 // but not present. This is meant to be sanity checked in other
3114 // functions.
3115 OptionPtr option = query->getOption(DHO_DHCP_SERVER_IDENTIFIER);
3116 if (!option) {
3117 return (true);
3118 }
3119 // Server identifier is present. Let's convert it to 4-byte address
3120 // and try to match with server identifiers used by the server.
3121 OptionCustomPtr option_custom =
3122 boost::dynamic_pointer_cast<OptionCustom>(option);
3123 // Unable to convert the option to the option type which encapsulates it.
3124 // We treat this as non-matching server id.
3125 if (!option_custom) {
3126 return (false);
3127 }
3128 // The server identifier option should carry exactly one IPv4 address.
3129 // If the option definition for the server identifier doesn't change,
3130 // the OptionCustom object should have exactly one IPv4 address and
3131 // this check is somewhat redundant. On the other hand, if someone
3132 // breaks option it may be better to check that here.
3133 if (option_custom->getDataFieldsNum() != 1) {
3134 return (false);
3135 }
3136
3137 // The server identifier MUST be an IPv4 address. If given address is
3138 // v6, it is wrong.
3139 IOAddress server_id = option_custom->readAddress();
3140 if (!server_id.isV4()) {
3141 return (false);
3142 }
3143
3144 // This function iterates over all interfaces on which the
3145 // server is listening to find the one which has a socket bound
3146 // to the address carried in the server identifier option.
3147 // This has some performance implications. However, given that
3148 // typically there will be just a few active interfaces the
3149 // performance hit should be acceptable. If it turns out to
3150 // be significant, we will have to cache server identifiers
3151 // when sockets are opened.
3152 if (IfaceMgr::instance().hasOpenSocket(server_id)) {
3153 return (true);
3154 }
3155
3156 // There are some cases when an administrator explicitly sets server
3157 // identifier (option 54) that should be used for a given, subnet,
3158 // network etc. It doesn't have to be an address assigned to any of
3159 // the server interfaces. Thus, we have to check if the server
3160 // identifier received is the one that we explicitly set in the
3161 // server configuration. At this point, we don't know which subnet
3162 // the client belongs to so we can't match the server id with any
3163 // subnet. We simply check if this server identifier is configured
3164 // anywhere. This should be good enough to eliminate exchanges
3165 // with other servers in the same network.
3166
3175
3177
3178 // Check if there is at least one subnet configured with this server
3179 // identifier.
3180 ConstCfgSubnets4Ptr cfg_subnets = cfg->getCfgSubnets4();
3181 if (cfg_subnets->hasSubnetWithServerId(server_id)) {
3182 return (true);
3183 }
3184
3185 // This server identifier is not configured for any of the subnets, so
3186 // check on the shared network level.
3187 CfgSharedNetworks4Ptr cfg_networks = cfg->getCfgSharedNetworks4();
3188 if (cfg_networks->hasNetworkWithServerId(server_id)) {
3189 return (true);
3190 }
3191
3192 // Finally, it is possible that the server identifier is specified
3193 // on the global level.
3194 ConstCfgOptionPtr cfg_global_options = cfg->getCfgOption();
3195 OptionCustomPtr opt_server_id = boost::dynamic_pointer_cast<OptionCustom>
3196 (cfg_global_options->get(DHCP4_OPTION_SPACE, DHO_DHCP_SERVER_IDENTIFIER).option_);
3197
3198 return (opt_server_id && (opt_server_id->readAddress() == server_id));
3199}
3200
3201void
3203 OptionPtr server_id = query->getOption(DHO_DHCP_SERVER_IDENTIFIER);
3204 switch (serverid) {
3205 case FORBIDDEN:
3206 if (server_id) {
3207 isc_throw(RFCViolation, "Server-id option was not expected, but "
3208 << "received in "
3209 << query->getName());
3210 }
3211 break;
3212
3213 case MANDATORY:
3214 if (!server_id) {
3215 isc_throw(RFCViolation, "Server-id option was expected, but not "
3216 " received in message "
3217 << query->getName());
3218 }
3219 break;
3220
3221 case OPTIONAL:
3222 // do nothing here
3223 ;
3224 }
3225
3226 // If there is HWAddress set and it is non-empty, then we're good
3227 if (query->getHWAddr() && !query->getHWAddr()->hwaddr_.empty()) {
3228 return;
3229 }
3230
3231 // There has to be something to uniquely identify the client:
3232 // either non-zero MAC address or client-id option present (or both)
3233 OptionPtr client_id = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
3234
3235 // If there's no client-id (or a useless one is provided, i.e. 0 length)
3236 if (!client_id || client_id->len() == client_id->getHeaderLen()) {
3237 isc_throw(RFCViolation, "Missing or useless client-id and no HW address "
3238 " provided in message "
3239 << query->getName());
3240 }
3241}
3242
3244 // Built-in vendor class processing
3245 boost::shared_ptr<OptionString> vendor_class =
3246 boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER));
3247
3248 if (!vendor_class) {
3249 return;
3250 }
3251
3252 pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
3253}
3254
3256 // All packets belongs to ALL.
3257 pkt->addClass("ALL");
3258
3259 // First: built-in vendor class processing.
3260 classifyByVendor(pkt);
3261
3262 // Run match expressions on classes not depending on KNOWN/UNKNOWN.
3263 evaluateClasses(pkt, false);
3264}
3265
3266void Dhcpv4Srv::evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known) {
3267 // Note getClientClassDictionary() cannot be null
3268 const ClientClassDictionaryPtr& dict =
3269 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3270 const ClientClassDefListPtr& defs_ptr = dict->getClasses();
3271 for (ClientClassDefList::const_iterator it = defs_ptr->cbegin();
3272 it != defs_ptr->cend(); ++it) {
3273 // Note second cannot be null
3274 const ExpressionPtr& expr_ptr = (*it)->getMatchExpr();
3275 // Nothing to do without an expression to evaluate
3276 if (!expr_ptr) {
3277 continue;
3278 }
3279 // Not the right time if only when required
3280 if ((*it)->getRequired()) {
3281 continue;
3282 }
3283 // Not the right pass.
3284 if ((*it)->getDependOnKnown() != depend_on_known) {
3285 continue;
3286 }
3287 // Evaluate the expression which can return false (no match),
3288 // true (match) or raise an exception (error)
3289 try {
3290 bool status = evaluateBool(*expr_ptr, *pkt);
3291 if (status) {
3292 LOG_INFO(options4_logger, EVAL_RESULT)
3293 .arg((*it)->getName())
3294 .arg(status);
3295 // Matching: add the class
3296 pkt->addClass((*it)->getName());
3297 } else {
3299 .arg((*it)->getName())
3300 .arg(status);
3301 }
3302 } catch (const Exception& ex) {
3303 LOG_ERROR(options4_logger, EVAL_RESULT)
3304 .arg((*it)->getName())
3305 .arg(ex.what());
3306 } catch (...) {
3307 LOG_ERROR(options4_logger, EVAL_RESULT)
3308 .arg((*it)->getName())
3309 .arg("get exception?");
3310 }
3311 }
3312}
3313
3315 // First collect required classes
3316 Pkt4Ptr query = ex.getQuery();
3317 ClientClasses classes = query->getClasses(true);
3318 Subnet4Ptr subnet = ex.getContext()->subnet_;
3319
3320 if (subnet) {
3321 // Begin by the shared-network
3322 SharedNetwork4Ptr network;
3323 subnet->getSharedNetwork(network);
3324 if (network) {
3325 const ClientClasses& to_add = network->getRequiredClasses();
3326 for (ClientClasses::const_iterator cclass = to_add.cbegin();
3327 cclass != to_add.cend(); ++cclass) {
3328 classes.insert(*cclass);
3329 }
3330 }
3331
3332 // Followed by the subnet
3333 const ClientClasses& to_add = subnet->getRequiredClasses();
3334 for(ClientClasses::const_iterator cclass = to_add.cbegin();
3335 cclass != to_add.cend(); ++cclass) {
3336 classes.insert(*cclass);
3337 }
3338
3339 // And finish by the pool
3340 Pkt4Ptr resp = ex.getResponse();
3342 if (resp) {
3343 addr = resp->getYiaddr();
3344 }
3345 if (!addr.isV4Zero()) {
3346 PoolPtr pool = subnet->getPool(Lease::TYPE_V4, addr, false);
3347 if (pool) {
3348 const ClientClasses& to_add = pool->getRequiredClasses();
3349 for (ClientClasses::const_iterator cclass = to_add.cbegin();
3350 cclass != to_add.cend(); ++cclass) {
3351 classes.insert(*cclass);
3352 }
3353 }
3354 }
3355
3356 // host reservation???
3357 }
3358
3359 // Run match expressions
3360 // Note getClientClassDictionary() cannot be null
3361 const ClientClassDictionaryPtr& dict =
3362 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3363 for (ClientClasses::const_iterator cclass = classes.cbegin();
3364 cclass != classes.cend(); ++cclass) {
3365 const ClientClassDefPtr class_def = dict->findClass(*cclass);
3366 if (!class_def) {
3367 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNDEFINED)
3368 .arg(*cclass);
3369 continue;
3370 }
3371 const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
3372 // Nothing to do without an expression to evaluate
3373 if (!expr_ptr) {
3374 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNTESTABLE)
3375 .arg(*cclass);
3376 continue;
3377 }
3378 // Evaluate the expression which can return false (no match),
3379 // true (match) or raise an exception (error)
3380 try {
3381 bool status = evaluateBool(*expr_ptr, *query);
3382 if (status) {
3383 LOG_INFO(options4_logger, EVAL_RESULT)
3384 .arg(*cclass)
3385 .arg(status);
3386 // Matching: add the class
3387 query->addClass(*cclass);
3388 } else {
3390 .arg(*cclass)
3391 .arg(status);
3392 }
3393 } catch (const Exception& ex) {
3394 LOG_ERROR(options4_logger, EVAL_RESULT)
3395 .arg(*cclass)
3396 .arg(ex.what());
3397 } catch (...) {
3398 LOG_ERROR(options4_logger, EVAL_RESULT)
3399 .arg(*cclass)
3400 .arg("get exception?");
3401 }
3402 }
3403}
3404
3405void
3407{
3408 // Iterate on the list of deferred option codes
3409 BOOST_FOREACH(const uint16_t& code, query->getDeferredOptions()) {
3411 // Iterate on client classes
3412 const ClientClasses& classes = query->getClasses();
3413 for (ClientClasses::const_iterator cclass = classes.cbegin();
3414 cclass != classes.cend(); ++cclass) {
3415 // Get the client class definition for this class
3416 const ClientClassDefPtr& ccdef =
3418 getClientClassDictionary()->findClass(*cclass);
3419 // If not found skip it
3420 if (!ccdef) {
3421 continue;
3422 }
3423 // If there is no option definition skip it
3424 if (!ccdef->getCfgOptionDef()) {
3425 continue;
3426 }
3427 def = ccdef->getCfgOptionDef()->get(DHCP4_OPTION_SPACE, code);
3428 // Stop at the first client class with a defition
3429 if (def) {
3430 break;
3431 }
3432 }
3433 // If not found try the global definition
3434 if (!def) {
3436 }
3437 if (!def) {
3439 }
3440 // Finish by last resort definition
3441 if (!def) {
3443 }
3444 // If not defined go to the next option
3445 if (!def) {
3446 continue;
3447 }
3448 // Get the existing option for its content and remove all
3449 OptionPtr opt = query->getOption(code);
3450 if (!opt) {
3451 // should not happen but do not crash anyway
3452 continue;
3453 }
3454 const OptionBuffer buf = opt->getData();
3455 while (query->delOption(code)) {
3456 // continue
3457 }
3458 // Unpack the option and add it
3459 opt = def->optionFactory(Option::V4, code, buf.cbegin(), buf.cend());
3460 query->addOption(opt);
3461 }
3462}
3463
3464void
3467 if (d2_mgr.ddnsEnabled()) {
3468 // Updates are enabled, so lets start the sender, passing in
3469 // our error handler.
3470 // This may throw so wherever this is called needs to ready.
3471 d2_mgr.startSender(boost::bind(&Dhcpv4Srv::d2ClientErrorHandler,
3472 this, _1, _2));
3473 }
3474}
3475
3476void
3479 if (d2_mgr.ddnsEnabled()) {
3480 // Updates are enabled, so lets stop the sender
3481 d2_mgr.stopSender();
3482 }
3483}
3484
3485void
3489 LOG_ERROR(ddns4_logger, DHCP4_DDNS_REQUEST_SEND_FAILED).
3490 arg(result).arg((ncr ? ncr->toText() : " NULL "));
3491 // We cannot communicate with kea-dhcp-ddns, suspend further updates.
3495}
3496
3497// Refer to config_report so it will be embedded in the binary
3499
3500std::string
3502 std::stringstream tmp;
3503
3504 tmp << VERSION;
3505 if (extended) {
3506 tmp << endl << EXTENDED_VERSION << endl;
3507 tmp << "linked with:" << endl;
3508 tmp << Logger::getVersion() << endl;
3509 tmp << CryptoLink::getVersion() << endl;
3510 tmp << "database:" << endl;
3511#ifdef HAVE_MYSQL
3512 tmp << MySqlLeaseMgr::getDBVersion() << endl;
3513#endif
3514#ifdef HAVE_PGSQL
3515 tmp << PgSqlLeaseMgr::getDBVersion() << endl;
3516#endif
3517#ifdef HAVE_CQL
3518 tmp << CqlLeaseMgr::getDBVersion() << endl;
3519#endif
3521
3522 // @todo: more details about database runtime
3523 }
3524
3525 return (tmp.str());
3526}
3527
3529 // Note that we're not bumping pkt4-received statistic as it was
3530 // increased early in the packet reception code.
3531
3532 string stat_name = "pkt4-unknown-received";
3533 try {
3534 switch (query->getType()) {
3535 case DHCPDISCOVER:
3536 stat_name = "pkt4-discover-received";
3537 break;
3538 case DHCPOFFER:
3539 // Should not happen, but let's keep a counter for it
3540 stat_name = "pkt4-offer-received";
3541 break;
3542 case DHCPREQUEST:
3543 stat_name = "pkt4-request-received";
3544 break;
3545 case DHCPACK:
3546 // Should not happen, but let's keep a counter for it
3547 stat_name = "pkt4-ack-received";
3548 break;
3549 case DHCPNAK:
3550 // Should not happen, but let's keep a counter for it
3551 stat_name = "pkt4-nak-received";
3552 break;
3553 case DHCPRELEASE:
3554 stat_name = "pkt4-release-received";
3555 break;
3556 case DHCPDECLINE:
3557 stat_name = "pkt4-decline-received";
3558 break;
3559 case DHCPINFORM:
3560 stat_name = "pkt4-inform-received";
3561 break;
3562 default:
3563 ; // do nothing
3564 }
3565 }
3566 catch (...) {
3567 // If the incoming packet doesn't have option 53 (message type)
3568 // or a hook set pkt4_receive_skip, then Pkt4::getType() may
3569 // throw an exception. That's ok, we'll then use the default
3570 // name of pkt4-unknown-received.
3571 }
3572
3574 static_cast<int64_t>(1));
3575}
3576
3578 // Increase generic counter for sent packets.
3580 static_cast<int64_t>(1));
3581
3582 // Increase packet type specific counter for packets sent.
3583 string stat_name;
3584 switch (response->getType()) {
3585 case DHCPOFFER:
3586 stat_name = "pkt4-offer-sent";
3587 break;
3588 case DHCPACK:
3589 stat_name = "pkt4-ack-sent";
3590 break;
3591 case DHCPNAK:
3592 stat_name = "pkt4-nak-sent";
3593 break;
3594 default:
3595 // That should never happen
3596 return;
3597 }
3598
3600 static_cast<int64_t>(1));
3601}
3602
3604 return (Hooks.hook_index_buffer4_receive_);
3605}
3606
3608 return (Hooks.hook_index_pkt4_receive_);
3609}
3610
3612 return (Hooks.hook_index_subnet4_select_);
3613}
3614
3616 return (Hooks.hook_index_lease4_release_);
3617}
3618
3620 return (Hooks.hook_index_pkt4_send_);
3621}
3622
3624 return (Hooks.hook_index_buffer4_send_);
3625}
3626
3628 return (Hooks.hook_index_lease4_decline_);
3629}
3630
3632 // Clear any packets held by the callhout handle store and
3633 // all parked packets
3634 isc::dhcp::Pkt4Ptr pkt4ptr_empty;
3635 isc::dhcp::getCalloutHandle(pkt4ptr_empty);
3637}
3638
3639} // namespace dhcp
3640} // 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:56
boost::shared_ptr< ClientContext4 > ClientContext4Ptr
Pointer to the ClientContext4.
@ 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:69
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition: cfgmgr.cc:65
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:154
static SubnetSelector initSelector(const Pkt4Ptr &query)
Build selector from a client's message.
Definition: cfg_subnets4.cc:80
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:68
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition: classify.cc:34
const_iterator cbegin() const
Iterator to the first element.
Definition: classify.h:81
const_iterator cend() const
Iterator to the past the end element.
Definition: classify.h:86
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:72
D2ClientMgr isolates Kea from the details of being a D2 client.
Definition: d2_client_mgr.h:79
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
const D2ClientConfigPtr & getD2ClientConfig() const
Fetches the DHCP-DDNS configuration pointer.
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 adjustFqdnFlags(const T &fqdn, T &fqdn_resp)
Set server FQDN flags based on configuration and a given FQDN.
void adjustDomainName(const T &fqdn, T &fqdn_resp)
Set server FQDN name based on configuration and a given FQDN.
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
std::string generateFqdn(const asiolink::IOAddress &address, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
std::string qualifyName(const std::string &partial_name, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
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:63
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
void initResponse()
Initializes the instance of the response message.
Definition: dhcp4_srv.cc:193
void setReservedMessageFields()
Sets reserved values of siaddr, sname and file in the server's response.
Definition: dhcp4_srv.cc:420
CfgOptionList & getCfgOptionList()
Returns the configured option list (non-const version)
Definition: dhcp4_srv.h:116
Dhcpv4Exchange(const AllocEnginePtr &alloc_engine, const Pkt4Ptr &query, const Subnet4Ptr &subnet)
Constructor.
Definition: dhcp4_srv.cc:123
Pkt4Ptr getResponse() const
Returns the pointer to the server's response.
Definition: dhcp4_srv.h:101
void initResponse4o6()
Initializes the DHCPv6 part of the response message.
Definition: dhcp4_srv.cc:219
void setReservedClientClasses()
Assigns classes retrieved from host reservation database.
Definition: dhcp4_srv.cc:409
void declineLease(const Lease4Ptr &lease, const Pkt4Ptr &decline, AllocEngine::ClientContext4Ptr &context)
Marks lease as declined.
Definition: dhcp4_srv.cc:2858
void classifyPacket(const Pkt4Ptr &pkt)
Assigns incoming packet to zero or more classes.
Definition: dhcp4_srv.cc:3255
void appendRequestedVendorOptions(Dhcpv4Exchange &ex)
Appends requested vendor options as requested by client.
Definition: dhcp4_srv.cc:1452
static void adjustIfaceData(Dhcpv4Exchange &ex)
Set IP/UDP and interface parameters for the DHCPv4 response.
Definition: dhcp4_srv.cc:2267
void run_one()
Main server processing step.
Definition: dhcp4_srv.cc:753
static uint16_t checkRelayPort(const Dhcpv4Exchange &ex)
Check if the relay port RAI sub-option was set in the query.
Definition: dhcp4_srv.cc:2254
bool acceptDirectRequest(const Pkt4Ptr &query) const
Check if a message sent by directly connected client should be accepted or discarded.
Definition: dhcp4_srv.cc:3011
virtual ~Dhcpv4Srv()
Destructor. Used during DHCPv4 service shutdown.
Definition: dhcp4_srv.cc:482
virtual Pkt4Ptr receivePacket(int timeout)
dummy wrapper around IfaceMgr::receive4
Definition: dhcp4_srv.cc:721
static void appendServerID(Dhcpv4Exchange &ex)
Adds server identifier option to the server's response.
Definition: dhcp4_srv.cc:1283
void shutdown()
Instructs the server to shut down.
Definition: dhcp4_srv.cc:511
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp4_srv.cc:3465
bool accept(const Pkt4Ptr &query) const
Checks whether received message should be processed or discarded.
Definition: dhcp4_srv.cc:2983
static int getHookIndexBuffer4Receive()
Returns the index for "buffer4_receive" hook point.
Definition: dhcp4_srv.cc:3603
Pkt4Ptr processRequest(Pkt4Ptr &request, AllocEngine::ClientContext4Ptr &context)
Processes incoming REQUEST and returns REPLY response.
Definition: dhcp4_srv.cc:2607
static void processStatsReceived(const Pkt4Ptr &query)
Class methods for DHCPv4-over-DHCPv6 handler.
Definition: dhcp4_srv.cc:3528
static int getHookIndexPkt4Send()
Returns the index for "pkt4_send" hook point.
Definition: dhcp4_srv.cc:3619
void processDecline(Pkt4Ptr &decline, AllocEngine::ClientContext4Ptr &context)
Process incoming DHCPDECLINE messages.
Definition: dhcp4_srv.cc:2784
static void evaluateClasses(const Pkt4Ptr &pkt, bool depend_on_known)
Evaluate classes.
Definition: dhcp4_srv.cc:3266
static int getHookIndexSubnet4Select()
Returns the index for "subnet4_select" hook point.
Definition: dhcp4_srv.cc:3611
static void processStatsSent(const Pkt4Ptr &response)
Updates statistics for transmitted packets.
Definition: dhcp4_srv.cc:3577
static int getHookIndexLease4Release()
Returns the index for "lease4_release" hook point.
Definition: dhcp4_srv.cc:3615
static void adjustRemoteAddr(Dhcpv4Exchange &ex)
Sets remote addresses for outgoing packet.
Definition: dhcp4_srv.cc:2348
static int getHookIndexPkt4Receive()
Returns the index for "pkt4_receive" hook point.
Definition: dhcp4_srv.cc:3607
void assignLease(Dhcpv4Exchange &ex)
Assigns a lease and appends corresponding options.
Definition: dhcp4_srv.cc:1864
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition: dhcp4_srv.h:234
void setFixedFields(Dhcpv4Exchange &ex)
Sets fixed fields of the outgoing packet.
Definition: dhcp4_srv.cc:2445
void appendBasicOptions(Dhcpv4Exchange &ex)
Append basic options if they are not present.
Definition: dhcp4_srv.cc:1540
void processClientName(Dhcpv4Exchange &ex)
Processes Client FQDN and Hostname Options sent by a client.
Definition: dhcp4_srv.cc:1586
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition: dhcp4_srv.h:911
void requiredClassify(Dhcpv4Exchange &ex)
Assigns incoming packet to zero or more classes (required pass).
Definition: dhcp4_srv.cc:3314
void createNameChangeRequests(const Lease4Ptr &lease, const Lease4Ptr &old_lease)
Creates NameChangeRequests which correspond to the lease which has been acquired.
Definition: dhcp4_srv.cc:1844
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:1263
bool acceptServerId(const Pkt4Ptr &pkt) const
Verifies if the server id belongs to our server.
Definition: dhcp4_srv.cc:3105
static const std::string VENDOR_CLASS_PREFIX
this is a prefix added to the content of vendor-class option
Definition: dhcp4_srv.h:613
Pkt4Ptr processInform(Pkt4Ptr &inform)
Processes incoming DHCPINFORM messages.
Definition: dhcp4_srv.cc:2934
void classifyByVendor(const Pkt4Ptr &pkt)
Assign class using vendor-class-identifier option.
Definition: dhcp4_srv.cc:3243
void appendRequestedOptions(Dhcpv4Exchange &ex)
Appends options requested by client.
Definition: dhcp4_srv.cc:1381
static std::string getVersion(bool extended)
returns Kea version on stdout and exit.
Definition: dhcp4_srv.cc:3501
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:603
void buildCfgOptionList(Dhcpv4Exchange &ex)
Build the configured option list.
Definition: dhcp4_srv.cc:1308
volatile bool shutdown_
indicates if shutdown is in progress.
Definition: dhcp4_srv.h:827
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition: dhcp4_srv.h:937
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:517
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:3486
virtual void sendPacket(const Pkt4Ptr &pkt)
dummy wrapper around IfaceMgr::send()
Definition: dhcp4_srv.cc:726
static int getHookIndexBuffer4Send()
Returns the index for "buffer4_send" hook point.
Definition: dhcp4_srv.cc:3623
void stopD2()
Stops DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp4_srv.cc:3477
bool acceptMessageType(const Pkt4Ptr &query) const
Check if received message type is valid for the server to process.
Definition: dhcp4_srv.cc:3049
static void sanityCheck(const Pkt4Ptr &query, RequirementLevel serverid)
Verifies if specified packet meets RFC requirements.
Definition: dhcp4_srv.cc:3202
void processPacket(Pkt4Ptr &query, Pkt4Ptr &rsp, bool allow_packet_park=true)
Process a single incoming DHCPv4 packet.
Definition: dhcp4_srv.cc:844
void discardPackets()
Discard all in-progress packets.
Definition: dhcp4_srv.cc:3631
static int getHookIndexLease4Decline()
Returns the index for "lease4_decline" hook point.
Definition: dhcp4_srv.cc:3627
void processRelease(Pkt4Ptr &release, AllocEngine::ClientContext4Ptr &context)
Processes incoming DHCPRELEASE messages.
Definition: dhcp4_srv.cc:2669
bool run()
Main server processing loop.
Definition: dhcp4_srv.cc:731
void processPacketPktSend(hooks::CalloutHandlePtr &callout_handle, Pkt4Ptr &query, Pkt4Ptr &rsp)
Executes pkt4_send callout.
Definition: dhcp4_srv.cc:1132
Pkt4Ptr processDiscover(Pkt4Ptr &discover)
Processes incoming DISCOVER and returns response.
Definition: dhcp4_srv.cc:2548
RequirementLevel
defines if certain option may, must or must not appear
Definition: dhcp4_srv.h:203
Dhcpv4Srv(uint16_t port=DHCP4_SERVER_PORT, const bool use_bcast=true, const bool direct_response_desired=true)
Default constructor.
Definition: dhcp4_srv.cc:444
void processPacketBufferSend(hooks::CalloutHandlePtr &callout_handle, Pkt4Ptr &rsp)
Executes buffer4_send callout and sends the response.
Definition: dhcp4_srv.cc:1190
void deferredUnpack(Pkt4Ptr &query)
Perform deferred option unpacking.
Definition: dhcp4_srv.cc:3406
IdentifierType
Type of the host identifier.
Definition: host.h:252
@ IDENT_HWADDR
Definition: host.h:253
@ IDENT_FLEX
Flexible host identifier.
Definition: host.h:257
@ IDENT_CLIENT_ID
Definition: host.h:256
@ IDENT_CIRCUIT_ID
Definition: host.h:255
std::string getIdentifierAsText() const
Returns host identifier in a textual form.
Definition: host.cc:239
uint16_t getSocket(const isc::dhcp::Pkt6 &pkt)
Return most suitable socket for transmitting specified IPv6 packet.
Definition: iface_mgr.cc:1622
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:312
bool send(const Pkt6Ptr &pkt)
Sends an IPv6 packet.
Definition: iface_mgr.cc:953
void closeSockets()
Closes all open sockets.
Definition: iface_mgr.cc:282
void setMatchingPacketFilter(const bool direct_response_desired=false)
Set Packet Filter object to handle send/receive packets.
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 void updateLease4(const Lease4Ptr &lease4)=0
Updates IPv4 lease.
virtual bool deleteLease(const isc::asiolink::IOAddress &addr)=0
Deletes a 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:144
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:205
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition: libdhcp++.cc:265
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:57
@ HR_DISABLED
None - host reservation is disabled.
Definition: network.h:95
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 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.
std::string getDomainName() const
Returns the domain-name in the text format.
Option with defined data fields represented as buffers that can be accessed using data field index.
Definition: option_custom.h:31
static unsigned int getLabelCount(const std::string &text_name)
Return the number of labels in the Name.
Option descriptor.
Definition: cfg_option.h:35
OptionPtr option_
Option instance.
Definition: cfg_option.h:38
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:27
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 std::string getDBVersion()
Local version of getDBVersion() class method.
Represents DHCPv4 packet.
Definition: pkt4.h:38
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:55
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:51
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition: option.h:52
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:467
@ NEXT_STEP_PARK
park the packet
@ NEXT_STEP_CONTINUE
continue normally
@ NEXT_STEP_DROP
drop the packet
@ NEXT_STEP_SKIP
skip the next processing step
static void unloadLibraries()
Unload libraries.
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static void park(const std::string &hook_name, T parked_object, std::function< void()> unpark_callback)
Park an object (packet).
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
static HooksManager & getHooksManager()
Get singleton hooks manager.
static void clearParkingLots()
Clears any parking packets.
Wrapper class around callout handle which automatically resets handle's state.
static std::string getVersion()
Version.
Definition: log/logger.cc:49
isc::util::SignalSetPtr signal_set_
A pointer to the object installing custom signal handlers.
Definition: daemon.h:238
virtual void handleSignal()
Invokes handler for the next received signal.
Definition: daemon.cc:60
static StatsMgr & instance()
Statistics Manager accessor method.
Definition: stats_mgr.cc:21
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
Contains declarations for loggers used by the DHCPv4 server component.
Dhcp4Hooks Hooks
Definition: dhcp4_srv.cc:117
Defines the Dhcp4o6Ipc class.
@ D6O_INTERFACE_ID
Definition: dhcp6.h:38
@ DHCPV6_DHCPV4_RESPONSE
Definition: dhcp6.h:237
#define DOCSIS3_V4_ORO
#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 addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
Definition: stats_mgr.cc:46
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
const char *const config_report[]
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:214
isc::log::Logger ddns4_logger(DHCP4_DDNS_LOGGER_NAME)
Logger for Hostname or FQDN processing.
Definition: dhcp4_log.h:115
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:464
boost::shared_ptr< Lease4Collection > Lease4CollectionPtr
A shared pointer to the collection of IPv4 leases.
Definition: lease.h:458
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
@ 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:178
@ DHO_DHCP_PARAMETER_REQUEST_LIST
Definition: dhcp4.h:124
@ DHO_FQDN
Definition: dhcp4.h:150
@ DHO_VIVSO_SUBOPTIONS
Definition: dhcp4.h:185
@ DHO_DHCP_RENEWAL_TIME
Definition: dhcp4.h:127
@ DHO_DHCP_LEASE_TIME
Definition: dhcp4.h:120
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
const int DBG_DHCP4_BASIC_DATA
Debug level used to log the traces with some basic data.
Definition: dhcp4_log.h:45
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:546
isc::log::Logger lease4_logger(DHCP4_LEASE_LOGGER_NAME)
Logger for lease allocation logic.
Definition: dhcp4_log.h:120
isc::log::Logger options4_logger(DHCP4_OPTIONS_LOGGER_NAME)
Logger for options parser.
Definition: dhcp4_log.h:109
const int DBG_DHCP4_DETAIL_DATA
This level is used to log the contents of packets received and sent.
Definition: dhcp4_log.h:56
boost::shared_ptr< AllocEngine > AllocEnginePtr
A pointer to the AllocEngine object.
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
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
Definition: srv_config.h:707
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
boost::shared_ptr< Pkt4o6 > Pkt4o6Ptr
A pointer to Pkt4o6 object.
Definition: pkt4o6.h:82
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:220
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
boost::shared_ptr< Option4ClientFqdn > Option4ClientFqdnPtr
A pointer to the Option4ClientFqdn object.
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
boost::shared_ptr< Pool > PoolPtr
a pointer to either IPv4 or IPv6 Pool
Definition: pool.h:405
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 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.
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition: duid.h:105
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:206
boost::shared_ptr< ClientClassDefList > ClientClassDefListPtr
Defines a pointer to a ClientClassDefList.
@ DHCPREQUEST
Definition: dhcp4.h:230
@ DHCP_TYPES_EOF
Definition: dhcp4.h:246
@ DHCPOFFER
Definition: dhcp4.h:229
@ DHCPDECLINE
Definition: dhcp4.h:231
@ DHCPNAK
Definition: dhcp4.h:233
@ DHCPRELEASE
Definition: dhcp4.h:234
@ DHCPDISCOVER
Definition: dhcp4.h:228
@ DHCP_NOTYPE
Message Type option missing.
Definition: dhcp4.h:227
@ DHCPINFORM
Definition: dhcp4.h:235
@ DHCPACK
Definition: dhcp4.h:232
const char *const * dhcp4_config_report
Definition: dhcp4_srv.cc:3498
boost::shared_ptr< const CfgSubnets4 > ConstCfgSubnets4Ptr
Const pointer.
Definition: cfg_subnets4.h:273
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
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:728
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:215
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:31
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
isc::asiolink::IOAddress getNetmask4(uint8_t len)
Generates an IPv4 netmask of specified length.
boost::shared_ptr< CfgSharedNetworks4 > CfgSharedNetworks4Ptr
Pointer to the configuration of IPv4 shared networks.
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 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:455
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:248
boost::shared_ptr< Option > OptionPtr
Definition: option.h:38
const int DBG_DHCP4_START
Debug level used to log information during server startup.
Definition: dhcp4_log.h:24
std::list< ConstCfgOptionPtr > CfgOptionList
Const pointer list.
Definition: cfg_option.h:503
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:500
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:65
boost::shared_ptr< StringSanitizer > StringSanitizerPtr
Definition: strutil.h:305
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
Defines the logger used by the top-level component of kea-dhcp-ddns.
#define DHCP4_OPTION_SPACE
Definition: option_space.h:16
Context information for the DHCPv4 lease allocation.
@ TYPE_V4
IPv4 lease.
Definition: lease.h:42
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
Holds information about socket.
Definition: socket_info.h:19
isc::asiolink::IOAddress addr_
Definition: socket_info.h:21
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.