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