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 #ifndef DHCPV4_SRV_H
8 #define DHCPV4_SRV_H
9 
10 #include <asiolink/io_service.h>
11 #include <dhcp/dhcp4.h>
12 #include <dhcp/option.h>
13 #include <dhcp/option_string.h>
14 #include <dhcp/option4_client_fqdn.h>
15 #include <dhcp/option_custom.h>
16 #include <dhcp/pkt4.h>
17 #include <dhcp_ddns/ncr_msg.h>
18 #include <dhcpsrv/alloc_engine.h>
19 #include <dhcpsrv/callout_handle_store.h>
20 #include <dhcpsrv/cb_ctl_dhcp4.h>
21 #include <dhcpsrv/cfg_option.h>
22 #include <dhcpsrv/d2_client_mgr.h>
23 #include <dhcpsrv/network_state.h>
24 #include <dhcpsrv/subnet.h>
25 #include <hooks/callout_handle.h>
26 #include <process/daemon.h>
27 
28 #include <functional>
29 #include <iostream>
30 #include <queue>
31 
32 // Undefine the macro OPTIONAL which is defined in some operating
33 // systems but conflicts with a member of the RequirementLevel enum in
34 // the server class.
35 
36 #ifdef OPTIONAL
37 #undef OPTIONAL
38 #endif
39 
40 namespace isc {
41 namespace dhcp {
42 
43 /// @brief DHCPv4 message exchange.
44 ///
45 /// This class represents the DHCPv4 message exchange. The message exchange
46 /// consists of the single client message, server response to this message
47 /// and the mechanisms to generate the server's response. The server creates
48 /// the instance of the @c Dhcpv4Exchange for each inbound message that it
49 /// accepts for processing.
50 ///
51 /// The use of the @c Dhcpv4Exchange object as a central repository of
52 /// information about the message exchange simplifies the API of the
53 /// @c Dhcpv4Srv class.
54 ///
55 /// Another benefit of using this class is that different methods of the
56 /// @c Dhcpv4Srv may share information. For example, the constructor of this
57 /// class selects the subnet and multiple methods of @c Dhcpv4Srv use this
58 /// subnet, without the need to select it again.
59 ///
60 /// @todo This is the initial version of this class. In the future a lot of
61 /// code from the @c Dhcpv4Srv class will be migrated here.
62 class Dhcpv4Exchange {
63 public:
64     /// @brief Constructor.
65     ///
66     /// The constructor selects the subnet for the query and checks for the
67     /// static host reservations for the client which has sent the message.
68     /// The information about the reservations is stored in the
69     /// @c AllocEngine::ClientContext4 object, which can be obtained by
70     /// calling the @c getContext.
71     ///
72     /// @param alloc_engine Pointer to the instance of the Allocation Engine
73     /// used by the server.
74     /// @param query Pointer to the client message.
75     /// @param subnet Pointer to the subnet to which the client belongs.
76     /// @param drop if it is true the packet will be dropped.
77     Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, const Pkt4Ptr& query,
78                    const Subnet4Ptr& subnet, bool& drop);
79 
80     /// @brief Initializes the instance of the response message.
81     ///
82     /// The type of the response depends on the type of the query message.
83     /// For the DHCPDISCOVER the DHCPOFFER is created. For the DHCPREQUEST
84     /// and DHCPINFORM the DHCPACK is created. For the DHCPRELEASE the
85     /// response is not initialized.
86     void initResponse();
87 
88     /// @brief Initializes the DHCPv6 part of the response message
89     ///
90     /// Called by initResponse() when the query is a DHCP4o6 message
91     void initResponse4o6();
92 
93     /// @brief Returns the pointer to the query from the client.
getQuery()94     Pkt4Ptr getQuery() const {
95         return (query_);
96     }
97 
98     /// @brief Returns the pointer to the server's response.
99     ///
100     /// The returned pointer is NULL if the query type is DHCPRELEASE or DHCPDECLINE.
getResponse()101     Pkt4Ptr getResponse() const {
102         return (resp_);
103     }
104 
105     /// @brief Removes the response message by resetting the pointer to NULL.
deleteResponse()106     void deleteResponse() {
107         resp_.reset();
108     }
109 
110     /// @brief Returns the copy of the context for the Allocation engine.
getContext()111     AllocEngine::ClientContext4Ptr getContext() const {
112         return (context_);
113     }
114 
115     /// @brief Returns the configured option list (non-const version)
getCfgOptionList()116     CfgOptionList& getCfgOptionList() {
117         return (cfg_option_list_);
118     }
119 
120     /// @brief Returns the configured option list (const version)
getCfgOptionList()121     const CfgOptionList& getCfgOptionList() const {
122         return (cfg_option_list_);
123     }
124 
125     /// @brief Sets reserved values of siaddr, sname and file in the
126     /// server's response.
127     void setReservedMessageFields();
128 
129     /// @brief Assigns classes retrieved from host reservation database.
130     ///
131     /// @param context pointer to the context.
132     static void setReservedClientClasses(AllocEngine::ClientContext4Ptr context);
133 
134     /// @brief Assigns classes retrieved from host reservation database
135     /// if they haven't been yet set.
136     ///
137     /// This function sets reserved client classes in case they haven't
138     /// been set after fetching host reservations from the database.
139     /// This is the case when the client has non-global host reservation
140     /// and the selected subnet belongs to a shared network.
141     void conditionallySetReservedClientClasses();
142 
143     /// @brief Assigns incoming packet to zero or more classes.
144     ///
145     /// @note This is done in two phases: first the content of the
146     /// vendor-class-identifier option is used as a class, by
147     /// calling @ref classifyByVendor(). Second, the classification match
148     /// expressions are evaluated. The resulting classes will be stored
149     /// in the packet (see @ref isc::dhcp::Pkt4::classes_ and
150     /// @ref isc::dhcp::Pkt4::inClass).
151     ///
152     /// @param pkt packet to be classified
153     static void classifyPacket(const Pkt4Ptr& pkt);
154 
155 private:
156 
157     /// @public
158     /// @brief Assign class using vendor-class-identifier option
159     ///
160     /// @note This is the first part of @ref classifyPacket
161     ///
162     /// @param pkt packet to be classified
163     static void classifyByVendor(const Pkt4Ptr& pkt);
164 
165     /// @brief Evaluate classes.
166     ///
167     /// @note Second part of the classification.
168     ///
169     /// Evaluate expressions of client classes: if it returns true the class
170     /// is added to the incoming packet.
171     ///
172     /// @param pkt packet to be classified.
173     /// @param depend_on_known if false classes depending on the KNOWN or
174     /// UNKNOWN classes are skipped, if true only these classes are evaluated.
175     static void evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known);
176 
177     /// @brief Copies default parameters from client's to server's message
178     ///
179     /// Some fields are copied from client's message into server's response,
180     /// e.g. client HW address, number of hops, transaction-id etc.
181     ///
182     /// @warning This message is called internally by @c initResponse and
183     /// thus it doesn't check if the resp_ value has been initialized. The
184     /// calling method is responsible for making sure that @c resp_ is
185     /// not NULL.
186     void copyDefaultFields();
187 
188     /// @brief Copies default options from client's to server's message
189     ///
190     /// Some options are copied from client's message into server's response,
191     /// e.g. Relay Agent Info option, Subnet Selection option etc.
192     ///
193     /// @warning This message is called internally by @c initResponse and
194     /// thus it doesn't check if the resp_ value has been initialized. The
195     /// calling method is responsible for making sure that @c resp_ is
196     /// not NULL.
197     void copyDefaultOptions();
198 
199     /// @brief Set host identifiers within a context.
200     ///
201     /// This method sets an ordered list of host identifier types and
202     /// values which the server should use to find host reservations.
203     /// The order of the set is determined by the configuration parameter,
204     /// host-reservation-identifiers
205     void setHostIdentifiers();
206 
207     /// @brief Pointer to the allocation engine used by the server.
208     AllocEnginePtr alloc_engine_;
209 
210     /// @brief Pointer to the DHCPv4 message sent by the client.
211     Pkt4Ptr query_;
212 
213     /// @brief Pointer to the DHCPv4 message to be sent to the client.
214     Pkt4Ptr resp_;
215 
216     /// @brief Context for use with allocation engine.
217     AllocEngine::ClientContext4Ptr context_;
218 
219     /// @brief Configured option list.
220     /// @note The configured option list is an *ordered* list of
221     /// @c CfgOption objects used to append options to the response.
222     CfgOptionList cfg_option_list_;
223 };
224 
225 /// @brief Type representing the pointer to the @c Dhcpv4Exchange.
226 typedef boost::shared_ptr<Dhcpv4Exchange> Dhcpv4ExchangePtr;
227 
228 
229 /// @brief DHCPv4 server service.
230 ///
231 /// This singleton class represents DHCPv4 server. It contains all
232 /// top-level methods and routines necessary for server operation.
233 /// In particular, it instantiates IfaceMgr, loads or generates DUID
234 /// that is going to be used as server-identifier, receives incoming
235 /// packets, processes them, manages leases assignment and generates
236 /// appropriate responses.
237 ///
238 /// This class does not support any controlling mechanisms directly.
239 /// See the derived \ref ControlledDhcpv4Srv class for support for
240 /// command and configuration updates over msgq.
241 class Dhcpv4Srv : public process::Daemon {
242 private:
243 
244     /// @brief Pointer to IO service used by the server.
245     asiolink::IOServicePtr io_service_;
246 
247 public:
248 
249     /// @brief defines if certain option may, must or must not appear
250     typedef enum {
251         FORBIDDEN,
252         MANDATORY,
253         OPTIONAL
254     } RequirementLevel;
255 
256     /// @brief Default constructor.
257     ///
258     /// Instantiates necessary services, required to run DHCPv4 server.
259     /// In particular, creates IfaceMgr that will be responsible for
260     /// network interaction. Will instantiate lease manager, and load
261     /// old or create new DUID. It is possible to specify alternate
262     /// port on which DHCPv4 server will listen on and alternate port
263     /// where DHCPv4 server sends all responses to. Those are mostly useful
264     /// for testing purposes. The Last two arguments of the constructor
265     /// should be left at default values for normal server operation.
266     /// They should be set to 'false' when creating an instance of this
267     /// class for unit testing because features they enable require
268     /// root privileges.
269     ///
270     /// @param server_port specifies port number to listen on
271     /// @param client_port specifies port number to send to
272     /// @param use_bcast configure sockets to support broadcast messages.
273     /// @param direct_response_desired specifies if it is desired to
274     /// use direct V4 traffic.
275     Dhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT,
276               uint16_t client_port = 0,
277               const bool use_bcast = true,
278               const bool direct_response_desired = true);
279 
280     /// @brief Destructor. Used during DHCPv4 service shutdown.
281     virtual ~Dhcpv4Srv();
282 
283     /// @brief Checks if the server is running in unit test mode.
284     ///
285     /// @return true if the server is running in unit test mode,
286     /// false otherwise.
inTestMode()287     bool inTestMode() const {
288         return (server_port_ == 0);
289     }
290 
291     /// @brief Returns pointer to the IO service used by the server.
getIOService()292     asiolink::IOServicePtr& getIOService() {
293         return (io_service_);
294     }
295 
296     /// @brief Returns pointer to the network state used by the server.
getNetworkState()297     NetworkStatePtr& getNetworkState() {
298         return (network_state_);
299     }
300 
301     /// @brief Returns an object which controls access to the configuration
302     /// backends.
303     ///
304     /// @return Pointer to the instance of the object which controls
305     /// access to the configuration backends.
getCBControl()306     CBControlDHCPv4Ptr getCBControl() const {
307         return (cb_control_);
308     }
309 
310     /// @brief returns Kea version on stdout and exit.
311     /// redeclaration/redefinition. @ref isc::process::Daemon::getVersion()
312     static std::string getVersion(bool extended);
313 
314     /// @brief Main server processing loop.
315     ///
316     /// Main server processing loop. Call the processing step routine
317     /// until shut down.
318     ///
319     /// @return The value returned by @c Daemon::getExitValue().
320     int run();
321 
322     /// @brief Main server processing step.
323     ///
324     /// Main server processing step. Receives one incoming packet, calls
325     /// the processing packet routing and (if necessary) transmits
326     /// a response.
327     void run_one();
328 
329     /// @brief Process a single incoming DHCPv4 packet and sends the response.
330     ///
331     /// It verifies correctness of the passed packet, calls per-type processXXX
332     /// methods, generates appropriate answer, sends the answer to the client.
333     ///
334     /// @param query A pointer to the packet to be processed.
335     void processPacketAndSendResponse(Pkt4Ptr& query);
336 
337     /// @brief Process a single incoming DHCPv4 packet and sends the response.
338     ///
339     /// It verifies correctness of the passed packet, calls per-type processXXX
340     /// methods, generates appropriate answer, sends the answer to the client.
341     ///
342     /// @param query A pointer to the packet to be processed.
343     void processPacketAndSendResponseNoThrow(Pkt4Ptr& query);
344 
345     /// @brief Process an unparked DHCPv4 packet and sends the response.
346     ///
347     /// @param callout_handle pointer to the callout handle.
348     /// @param query A pointer to the packet to be processed.
349     /// @param rsp A pointer to the response.
350     void sendResponseNoThrow(hooks::CalloutHandlePtr& callout_handle,
351                              Pkt4Ptr& query, Pkt4Ptr& rsp);
352 
353     /// @brief Process a single incoming DHCPv4 packet.
354     ///
355     /// It verifies correctness of the passed packet, calls per-type processXXX
356     /// methods, generates appropriate answer.
357     ///
358     /// @param query A pointer to the packet to be processed.
359     /// @param rsp A pointer to the response.
360     /// @param allow_packet_park Indicates if parking a packet is allowed.
361     void processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp,
362                        bool allow_packet_park = true);
363 
364     /// @brief Process a single incoming DHCPv4 query.
365     ///
366     /// It calls per-type processXXX methods, generates appropriate answer.
367     ///
368     /// @param query A pointer to the packet to be processed.
369     /// @param rsp A pointer to the response.
370     /// @param allow_packet_park Indicates if parking a packet is allowed.
371     void processDhcp4Query(Pkt4Ptr& query, Pkt4Ptr& rsp,
372                            bool allow_packet_park);
373 
374     /// @brief Process a single incoming DHCPv4 query.
375     ///
376     /// It calls per-type processXXX methods, generates appropriate answer,
377     /// sends the answer to the client.
378     ///
379     /// @param query A pointer to the packet to be processed.
380     /// @param rsp A pointer to the response.
381     /// @param allow_packet_park Indicates if parking a packet is allowed.
382     void processDhcp4QueryAndSendResponse(Pkt4Ptr& query, Pkt4Ptr& rsp,
383                                           bool allow_packet_park);
384 
385     /// @brief Instructs the server to shut down.
386     void shutdown() override;
387 
388     ///
389     /// @name Public accessors returning values required to (re)open sockets.
390     ///
391     //@{
392     ///
393     /// @brief Get UDP port on which server should listen.
394     ///
395     /// Typically, server listens on UDP port number 67. Other ports are used
396     /// for testing purposes only.
397     ///
398     /// @return UDP port on which server should listen.
getServerPort()399     uint16_t getServerPort() const {
400         return (server_port_);
401     }
402 
403     /// @brief Return bool value indicating that broadcast flags should be set
404     /// on sockets.
405     ///
406     /// @return A bool value indicating that broadcast should be used (if true).
useBroadcast()407     bool useBroadcast() const {
408         return (use_bcast_);
409     }
410     //@}
411 
412     /// @brief Starts DHCP_DDNS client IO if DDNS updates are enabled.
413     ///
414     /// If updates are enabled, it instructs the D2ClientMgr singleton to
415     /// enter send mode.  If D2ClientMgr encounters errors it may throw
416     /// D2ClientError. This method does not catch exceptions.
417     void startD2();
418 
419     /// @brief Stops DHCP_DDNS client IO if DDNS updates are enabled.
420     ///
421     /// If updates are enabled, it instructs the D2ClientMgr singleton to
422     /// leave send mode.  If D2ClientMgr encounters errors it may throw
423     /// D2ClientError. This method does not catch exceptions.
424     void stopD2();
425 
426     /// @brief Implements the error handler for DHCP_DDNS IO errors
427     ///
428     /// Invoked when a NameChangeRequest send to kea-dhcp-ddns completes with
429     /// a failed status.  These are communications errors, not data related
430     /// failures.
431     ///
432     /// This method logs the failure and then suspends all further updates.
433     /// Updating can only be restored by reconfiguration or restarting the
434     /// server.  There is currently no retry logic so the first IO error that
435     /// occurs will suspend updates.
436     /// @todo We may wish to make this more robust or sophisticated.
437     ///
438     /// @param result Result code of the send operation.
439     /// @param ncr NameChangeRequest which failed to send.
440     virtual void d2ClientErrorHandler(const dhcp_ddns::
441                                       NameChangeSender::Result result,
442                                       dhcp_ddns::NameChangeRequestPtr& ncr);
443 
444     /// @brief Discards parked packets
445     /// Clears the packet parking lots of all packets.
446     /// Called during reconfigure and shutdown.
447     void discardPackets();
448 
449     /// @brief Returns value of the test_send_responses_to_source_ flag.
450     ///
451     /// @return value of the test_send_responses_to_source_ flag.
getSendResponsesToSource()452     bool getSendResponsesToSource() const {
453         return (test_send_responses_to_source_);
454     }
455 
456 protected:
457 
458     /// @name Functions filtering and sanity-checking received messages.
459     ///
460     /// @todo These functions are supposed to be moved to a new class which
461     /// will manage different rules for accepting and rejecting messages.
462     /// Perhaps ticket #3116 is a good opportunity to do it.
463     ///
464     //@{
465     /// @brief Checks whether received message should be processed or discarded.
466     ///
467     /// This function checks whether received message should be processed or
468     /// discarded. It should be called on the beginning of message processing
469     /// (just after the message has been decoded). This message calls a number
470     /// of other functions which check whether message should be processed,
471     /// using different criteria.
472     ///
473     /// This function should be extended when new criteria for accepting
474     /// received message have to be implemented. This function is meant to
475     /// aggregate all early filtering checks on the received message. By having
476     /// a single function like this, we are avoiding bloat of the server's main
477     /// loop.
478     ///
479     /// @warning This function should remain exception safe.
480     ///
481     /// @param query Received message.
482     ///
483     /// @return true if the message should be further processed, or false if
484     /// the message should be discarded.
485     bool accept(const Pkt4Ptr& query) const;
486 
487     /// @brief Check if a message sent by directly connected client should be
488     /// accepted or discarded.
489     ///
490     /// This function checks if the received message is from directly connected
491     /// client. If it is, it checks that it should be processed or discarded.
492     ///
493     /// Note that this function doesn't validate all addresses being carried in
494     /// the message. The primary purpose of this function is to filter out
495     /// direct messages in the local network for which there is no suitable
496     /// subnet configured. For example, this function accepts unicast messages
497     /// because unicasts may be used by clients located in remote networks to
498     /// to renew existing leases. If their notion of address is wrong, the
499     /// server will have to sent a NAK, instead of dropping the message.
500     /// Detailed validation of such messages is performed at later stage of
501     /// processing.
502     ///
503     /// This function accepts the following messages:
504     /// - all valid relayed messages,
505     /// - all unicast messages,
506     /// - all broadcast messages except DHCPINFORM received on the interface
507     /// for which the suitable subnet exists (is configured).
508     /// - all DHCPINFORM messages with source address or ciaddr set.
509     ///
510     /// @param query Message sent by a client.
511     ///
512     /// @return true if message is accepted for further processing, false
513     /// otherwise.
514     bool acceptDirectRequest(const Pkt4Ptr& query) const;
515 
516     /// @brief Check if received message type is valid for the server to
517     /// process.
518     ///
519     /// This function checks that the received message type belongs to
520     /// the range of types recognized by the server and that the
521     /// message of this type should be processed by the server.
522     ///
523     /// The messages types accepted for processing are:
524     /// - Discover
525     /// - Request
526     /// - Release
527     /// - Decline
528     /// - Inform
529     ///
530     /// @param query Message sent by a client.
531     ///
532     /// @return true if message is accepted for further processing, false
533     /// otherwise.
534     bool acceptMessageType(const Pkt4Ptr& query) const;
535 
536     /// @brief Verifies if the server id belongs to our server.
537     ///
538     /// This function checks if the server identifier carried in the specified
539     /// DHCPv4 message belongs to this server. If the server identifier option
540     /// is absent or the value carried by this option is equal to one of the
541     /// server identifiers used by the server, the true is returned. If the
542     /// server identifier option is present, but it doesn't match any server
543     /// identifier used by this server, the false value is returned.
544     ///
545     /// @param pkt DHCPv4 message which server identifier is to be checked.
546     ///
547     /// @return true, if the server identifier is absent or matches one of the
548     /// server identifiers that the server is using; false otherwise.
549     bool acceptServerId(const Pkt4Ptr& pkt) const;
550     //@}
551 
552     /// @brief Verifies if specified packet meets RFC requirements
553     ///
554     /// Checks if mandatory option is really there, that forbidden option
555     /// is not there, and that client-id or server-id appears only once.
556     ///
557     /// @param query Pointer to the client's message.
558     /// @param serverid expectation regarding server-id option
559     /// @throw RFCViolation if any issues are detected
560     static void sanityCheck(const Pkt4Ptr& query, RequirementLevel serverid);
561 
562     /// @brief Processes incoming DISCOVER and returns response.
563     ///
564     /// Processes received DISCOVER message and verifies that its sender
565     /// should be served. In particular, a lease is selected and sent
566     /// as an offer to a client if it should be served.
567     ///
568     /// @param discover DISCOVER message received from client
569     ///
570     /// @return OFFER message or NULL
571     Pkt4Ptr processDiscover(Pkt4Ptr& discover);
572 
573     /// @brief Processes incoming REQUEST and returns REPLY response.
574     ///
575     /// Processes incoming REQUEST message and verifies that its sender
576     /// should be served. In particular, verifies that requested lease
577     /// is valid, not expired, not reserved, not used by other client and
578     /// that requesting client is allowed to use it.
579     ///
580     /// Returns ACK message, NAK message, or NULL
581     ///
582     /// @param request a message received from client
583     /// @param [out] context pointer to the client context where allocated
584     /// and deleted leases are stored.
585     ///
586     /// @return ACK or NAK message
587     Pkt4Ptr processRequest(Pkt4Ptr& request, AllocEngine::ClientContext4Ptr& context);
588 
589     /// @brief Processes incoming DHCPRELEASE messages.
590     ///
591     /// In DHCPv4, server does not respond to RELEASE messages, therefore
592     /// this function does not return anything.
593     ///
594     /// @param release message received from client
595     /// @param [out] context pointer to the client context where released
596     /// lease is stored.
597     void processRelease(Pkt4Ptr& release, AllocEngine::ClientContext4Ptr& context);
598 
599     /// @brief Process incoming DHCPDECLINE messages.
600     ///
601     /// This method processes incoming DHCPDECLINE. In particular, it extracts
602     /// Requested IP Address option, checks that the address really belongs to
603     /// the client and if it does, calls @ref declineLease.
604     ///
605     /// @param decline message received from client
606     /// @param [out] context pointer to the client context where declined
607     /// lease is stored.
608     void processDecline(Pkt4Ptr& decline, AllocEngine::ClientContext4Ptr& context);
609 
610     /// @brief Processes incoming DHCPINFORM messages.
611     ///
612     /// @param inform message received from client
613     ///
614     /// @return DHCPACK to be sent to the client.
615     Pkt4Ptr processInform(Pkt4Ptr& inform);
616 
617     /// @brief Build the configured option list
618     ///
619     /// @note The configured option list is an *ordered* list of
620     /// @c CfgOption objects used to append options to the response.
621     ///
622     /// @param ex The exchange where the configured option list is cached
623     void buildCfgOptionList(Dhcpv4Exchange& ex);
624 
625     /// @brief Appends options requested by client.
626     ///
627     /// This method assigns options that were requested by client
628     /// (sent in PRL) or are enforced by server.
629     ///
630     /// @param ex The exchange holding both the client's message and the
631     /// server's response.
632     void appendRequestedOptions(Dhcpv4Exchange& ex);
633 
634     /// @brief Appends requested vendor options as requested by client.
635     ///
636     /// This method is similar to \ref appendRequestedOptions(), but uses
637     /// vendor options. The major difference is that vendor-options use
638     /// its own option spaces (there may be more than one distinct set of vendor
639     /// options, each with unique vendor-id). Vendor options are requested
640     /// using separate options within their respective vendor-option spaces.
641     ///
642     /// @param ex The exchange holding both the client's message and the
643     /// server's response.
644     void appendRequestedVendorOptions(Dhcpv4Exchange& ex);
645 
646     /// @brief Assigns a lease and appends corresponding options
647     ///
648     /// This method chooses the most appropriate lease for requesting
649     /// client and assigning it. Options corresponding to the lease
650     /// are added to specific message.
651     ///
652     /// This method may reset the pointer to the response in the @c ex object
653     /// to indicate that the response should not be sent to the client.
654     /// The caller must check if the response is is null after calling
655     /// this method.
656     ///
657     /// The response type in the @c ex object may be set to DHCPACK or DHCPNAK.
658     ///
659     /// @param ex DHCPv4 exchange holding the client's message to be checked.
660     void assignLease(Dhcpv4Exchange& ex);
661 
662     /// @brief Update client name and DNS flags in the lease and response
663     ///
664     /// There are two cases when the client name (FQDN or hostname) and DNS
665     /// flags need to updated after the lease has been allocated:
666     /// 1. If the name is being generated from the lease address
667     /// 2. If the allocation changed the chosen subnet
668     ///
669     /// In the first case this function will generate the name from the
670     /// lease address.  In either case, the name and DNS flags are updated
671     /// in the lease and in the response packet.
672     ///
673     /// @param ctx reference to the client context
674     /// @param lease reference to the client lease
675     /// @param query reference to the client query
676     /// @param resp reference to the client response
677     /// @param client_name_changed - true if the new values are already in
678     /// the lease
679     void postAllocateNameUpdate(const AllocEngine::ClientContext4Ptr& ctx,
680                                 const Lease4Ptr& lease, const Pkt4Ptr& query,
681                                 const Pkt4Ptr& resp, bool client_name_changed);
682 
683     /// @brief Adds the T1 and T2 timers to the outbound response as appropriate
684     ///
685     /// This method determines if either of the timers T1 (option 58) and T2
686     /// (option 59) should be sent to the client.  It is influenced by the
687     /// lease's subnet's values for renew-timer, rebind-timer,
688     /// calculate-tee-times, t1-percent, and t2-percent as follows:
689     ///
690     /// By default neither T1 nor T2 will be sent.
691     ///
692     /// T2:
693     ///
694     /// If rebind-timer is set use its value, otherwise if calculate-tee-times
695     /// is true use the value given by valid lease time * t2-percent.  Either
696     /// way the value will only be sent if it is less than the valid lease time.
697     ///
698     /// T1:
699     ///
700     /// If renew-timer is set use its value, otherwise if calculate-tee-times
701     /// is true use the value given by valid lease time * t1-percent.  Either
702     /// way the value will only be sent if it is less than T2 when T2 is being
703     /// sent, or less than the valid lease time if T2 is not being sent.
704     ///
705     /// @param lease lease being assigned to the client
706     /// @param subnet the subnet to which the lease belongs
707     /// @param resp outbound response for the client to which timers are added.
708     void setTeeTimes(const Lease4Ptr& lease, const Subnet4Ptr& subnet, Pkt4Ptr resp);
709 
710     /// @brief Append basic options if they are not present.
711     ///
712     /// This function adds the following basic options if they
713     /// are not yet added to the response message:
714     /// - Subnet Mask,
715     /// - Router,
716     /// - Name Server,
717     /// - Domain Name,
718     /// - Server Identifier.
719     ///
720     /// @param ex DHCPv4 exchange holding the client's message to be checked.
721     void appendBasicOptions(Dhcpv4Exchange& ex);
722 
723     /// @brief Sets fixed fields of the outgoing packet.
724     ///
725     /// If the incoming packets belongs to a class and that class defines
726     /// next-server, server-hostname or boot-file-name, we need to set the
727     /// siaddr, sname or filename fields in the outgoing packet. Also, those
728     /// values can be defined for subnet or in reservations. The values
729     /// defined in reservation takes precedence over class values, which
730     /// in turn take precedence over subnet values.
731     ///
732     /// @param ex DHCPv4 exchange holding the client's message and the server's
733     ///           response to be adjusted.
734     void setFixedFields(Dhcpv4Exchange& ex);
735 
736     /// @brief Processes Client FQDN and Hostname Options sent by a client.
737     ///
738     /// Client may send Client FQDN or Hostname option to communicate its name
739     /// to the server. Server may use this name to perform DNS update for the
740     /// lease being assigned to a client. If server takes responsibility for
741     /// updating DNS for a client it may communicate it by sending the Client
742     /// FQDN or Hostname %Option back to the client. Server select a different
743     /// name than requested by a client to update DNS. In such case, the server
744     /// stores this different name in its response.
745     ///
746     /// Client should not send both Client FQDN and Hostname options. However,
747     /// if client sends both options, server should prefer Client FQDN option
748     /// and ignore the Hostname option. If Client FQDN option is not present,
749     /// the Hostname option is processed.
750     ///
751     /// The Client FQDN %Option is processed by this function as described in
752     /// RFC4702.
753     ///
754     /// In response to a Hostname %Option sent by a client, the server may send
755     /// Hostname option with the same or different hostname. If different
756     /// hostname is sent, it is an indication to the client that server has
757     /// overridden the client's preferred name and will rather use this
758     /// different name to update DNS. However, since Hostname option doesn't
759     /// carry an information whether DNS update will be carried by the server
760     /// or not, the client is responsible for checking whether DNS update
761     /// has been performed.
762     ///
763     /// After successful processing options stored in the first parameter,
764     /// this function may add Client FQDN or Hostname option to the response
765     /// message. In some cases, server may cease to add any options to the
766     /// response, i.e. when server doesn't support DNS updates.
767     ///
768     /// This function does not throw. It simply logs the debug message if the
769     /// processing of the FQDN or Hostname failed.
770     ///
771     /// @param ex The exchange holding both the client's message and the
772     /// server's response.
773     void processClientName(Dhcpv4Exchange& ex);
774 
775     /// @brief This function sets statistics related to DHCPv4 packets processing
776     /// to their initial values.
777     ///
778     /// All of the statistics observed by the DHCPv4 server and with the names
779     /// like "pkt4-" are reset to 0. This function must be invoked in the class
780     /// constructor.
781     void setPacketStatisticsDefaults();
782 
783     /// @brief Sets value of the test_send_responses_to_source_ flag.
784     ///
785     /// @param value new value of the test_send_responses_to_source_ flag.
setSendResponsesToSource(bool value)786     void setSendResponsesToSource(bool value) {
787         test_send_responses_to_source_ = value;
788     }
789 
790 public:
791 
792     /// @brief this is a prefix added to the content of vendor-class option
793     ///
794     /// If incoming packet has a vendor class option, its content is
795     /// prepended with this prefix and then interpreted as a class.
796     /// For example, a packet that sends vendor class with value of "FOO"
797     /// will cause the packet to be assigned to class VENDOR_CLASS_FOO.
798     static const std::string VENDOR_CLASS_PREFIX;
799 
800 private:
801     /// @brief Process Client FQDN %Option sent by a client.
802     ///
803     /// This function is called by the @c Dhcpv4Srv::processClientName when
804     /// the client has sent the FQDN option in its message to the server.
805     /// It comprises the actual logic to parse the FQDN option and prepare
806     /// the FQDN option to be sent back to the client in the server's
807     /// response.
808     ///
809     /// @param ex The exchange holding both the client's message and the
810     /// server's response.
811     void processClientFqdnOption(Dhcpv4Exchange& ex);
812 
813     /// @brief Process Hostname %Option sent by a client.
814     ///
815     /// This method is called by the @c Dhcpv4Srv::processClientName to
816     /// create an instance of the Hostname option to be returned to the
817     /// client. If this instance is created it is included in the response
818     /// message within the @c Dhcpv4Exchange object passed as an argument.
819     ///
820     /// The Hostname option instance is created if the client has included
821     /// Hostname option in its query to the server or if the client has
822     /// included Hostname option code in the Parameter Request List option.
823     /// In the former case, the server can use the Hostname supplied by the
824     /// client or replace it with a new hostname, depending on the server's
825     /// configuration. A reserved hostname takes precedence over a hostname
826     /// supplied by the client or auto generated hostname.
827     ///
828     /// If the 'qualifying-suffix' parameter is specified, its value is used
829     /// to qualify a hostname. For example, if the host reservation contains
830     /// a hostname 'marcin-laptop', and the qualifying suffix is
831     /// 'example.isc.org', the hostname returned to the client will be
832     /// 'marcin-laptop.example.isc.org'. If the 'qualifying-suffix' is not
833     /// specified (empty), the reserved hostname is returned to the client
834     /// unqualified.
835     ///
836     /// The 'qualifying-suffix' value is also used to qualify the hostname
837     /// supplied by the client, when this hostname is unqualified,
838     /// e.g. 'laptop-x'. If the supplied hostname is qualified, e.g.
839     /// 'laptop-x.example.org', the qualifying suffix will not be appended
840     /// to it.
841     ///
842     /// @param ex The exchange holding both the client's message and the
843     /// server's response.
844     void processHostnameOption(Dhcpv4Exchange& ex);
845 
846     /// @public
847     /// @brief Marks lease as declined.
848     ///
849     /// This method moves a lease to declined state with all the steps involved:
850     /// - trigger DNS removal (if necessary)
851     /// - disassociate the client information
852     /// - update lease in the database (switch to DECLINED state)
853     /// - increase necessary statistics
854     /// - call lease4_decline hook
855     ///
856     /// @param lease lease to be declined
857     /// @param decline client's message
858     /// @param context reference to a client context
859     void declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline,
860                       AllocEngine::ClientContext4Ptr& context);
861 
862 protected:
863 
864     /// @brief Creates NameChangeRequests which correspond to the lease
865     /// which has been acquired.
866     ///
867     /// If this function is called when an existing lease is renewed, it
868     /// may generate NameChangeRequest to remove existing DNS entries which
869     /// correspond to the old lease instance. This function may cease to
870     /// generate NameChangeRequests if the notion of the client's FQDN hasn't
871     /// changed between an old and new lease.
872     ///
873     /// @param lease A pointer to the new lease which has been acquired.
874     /// @param old_lease A pointer to the instance of the old lease which has
875     /// @param ddns_params DDNS configuration parameters
876     /// been replaced by the new lease passed in the first argument. The NULL
877     /// value indicates that the new lease has been allocated, rather than
878     /// lease being renewed.
879     void createNameChangeRequests(const Lease4Ptr& lease,
880                                   const Lease4Ptr& old_lease,
881                                   const DdnsParams& ddns_params);
882 
883     /// @brief Attempts to renew received addresses
884     ///
885     /// Attempts to renew existing lease. This typically includes finding a lease that
886     /// corresponds to the received address. If no such lease is found, a status code
887     /// response is generated.
888     ///
889     /// @param renew client's message asking for renew
890     /// @param reply server's response (ACK or NAK)
891     void renewLease(const Pkt4Ptr& renew, Pkt4Ptr& reply);
892 
893     /// @brief Adds server identifier option to the server's response.
894     ///
895     /// This method adds a server identifier to the DHCPv4 message if it doesn't
896     /// exist yet. This is set to the local address on which the client's query has
897     /// been received with the exception of broadcast traffic and DHCPv4o6 query for
898     /// which a socket on the particular interface is found and its address is used
899     /// as server id.
900     ///
901     /// @note This method doesn't throw exceptions by itself but the underlying
902     /// classes being used my throw. The reason for this method to not sanity
903     /// check the specified message is that it is meant to be called internally
904     /// by the @c Dhcpv4Srv class.
905     ///
906     /// @note This method is static because it is not dependent on the class
907     /// state.
908     ///
909     /// @param ex The exchange holding both the client's message and the
910     /// server's response.
911     static void appendServerID(Dhcpv4Exchange& ex);
912 
913     /// @brief Check if the relay port RAI sub-option was set in the query.
914     ///
915     /// @param ex The exchange holding the client's message
916     /// @return the port to use to join the relay or 0 for the default
917     static uint16_t checkRelayPort(const Dhcpv4Exchange& ex);
918 
919     /// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
920     ///
921     /// This method sets the following parameters for the DHCPv4 message being
922     /// sent to a client:
923     /// - client unicast or a broadcast address,
924     /// - client or relay port,
925     /// - server address,
926     /// - server port,
927     /// - name and index of the interface which is to be used to send the
928     /// message.
929     ///
930     /// Internally it calls the @c Dhcpv4Srv::adjustRemoteAddr to figure
931     /// out the destination address (client unicast address or broadcast
932     /// address).
933     ///
934     /// The destination port is always DHCPv4 client (68) or relay (67) port,
935     /// depending if the response will be sent directly to a client, unless
936     /// a client port was enforced from the command line.
937     ///
938     /// The source port is always set to DHCPv4 server port (67).
939     ///
940     /// The interface selected for the response is always the same as the
941     /// one through which the query has been received.
942     ///
943     /// The source address for the response is the IPv4 address assigned to
944     /// the interface being used to send the response. This function uses
945     /// @c IfaceMgr to get the socket bound to the IPv4 address on the
946     /// particular interface.
947     ///
948     /// @note This method is static because it is not dependent on the class
949     /// state.
950     ///
951     /// @param ex The exchange holding both the client's message and the
952     /// server's response.
953     void adjustIfaceData(Dhcpv4Exchange& ex);
954 
955     /// @brief Sets remote addresses for outgoing packet.
956     ///
957     /// This method sets the local and remote addresses on outgoing packet.
958     /// The addresses being set depend on the following conditions:
959     /// - has incoming packet been relayed,
960     /// - is direct response to a client without address supported,
961     /// - type of the outgoing packet,
962     /// - broadcast flag set in the incoming packet.
963     ///
964     /// @warning This method does not check whether provided packet pointers
965     /// are valid. Make sure that pointers are correct before calling this
966     /// function.
967     ///
968     /// @param ex The exchange holding both the client's message and the
969     /// server's response.
970     void adjustRemoteAddr(Dhcpv4Exchange& ex);
971 
972     /// @brief converts server-id to text
973     /// Converts content of server-id option to a text representation, e.g.
974     /// "192.0.2.1"
975     ///
976     /// @param opt option that contains server-id
977     /// @return string representation
978     static std::string srvidToString(const OptionPtr& opt);
979 
980     /// @brief Selects a subnet for a given client's packet.
981     ///
982     /// If selectSubnet is called to simply do sanity checks (check if a
983     /// subnet would be selected), then there is no need to call hooks,
984     /// as this will happen later (when selectSubnet is called again).
985     /// In such case the sanity_only should be set to true.
986     ///
987     /// @param query client's message
988     /// @param drop if it is true the packet will be dropped
989     /// @param sanity_only if it is true the callout won't be called
990     /// @return selected subnet (or NULL if no suitable subnet was found)
991     isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& query,
992                                        bool& drop,
993                                        bool sanity_only = false) const;
994 
995     /// @brief Selects a subnet for a given client's DHCP4o6 packet.
996     ///
997     /// If selectSubnet is called to simply do sanity checks (check if a
998     /// subnet would be selected), then there is no need to call hooks,
999     /// as this will happen later (when selectSubnet is called again).
1000     /// In such case the sanity_only should be set to true.
1001     ///
1002     /// @param query client's message
1003     /// @param drop if it is true the packet will be dropped
1004     /// @param sanity_only if it is true the callout won't be called
1005     /// @return selected subnet (or NULL if no suitable subnet was found)
1006     isc::dhcp::Subnet4Ptr selectSubnet4o6(const Pkt4Ptr& query,
1007                                           bool& drop,
1008                                           bool sanity_only = false) const;
1009 
1010     /// @brief dummy wrapper around IfaceMgr::receive4
1011     ///
1012     /// This method is useful for testing purposes, where its replacement
1013     /// simulates reception of a packet. For that purpose it is protected.
1014     virtual Pkt4Ptr receivePacket(int timeout);
1015 
1016     /// @brief dummy wrapper around IfaceMgr::send()
1017     ///
1018     /// This method is useful for testing purposes, where its replacement
1019     /// simulates transmission of a packet. For that purpose it is protected.
1020     virtual void sendPacket(const Pkt4Ptr& pkt);
1021 
1022     /// @brief Assigns incoming packet to zero or more classes.
1023     ///
1024     /// @note This is done in two phases: first the content of the
1025     /// vendor-class-identifier option is used as a class, by
1026     /// calling @ref classifyByVendor(). Second classification match
1027     /// expressions are evaluated. The resulting classes will be stored
1028     /// in the packet (see @ref isc::dhcp::Pkt4::classes_ and
1029     /// @ref isc::dhcp::Pkt4::inClass).
1030     ///
1031     /// @param pkt packet to be classified
1032     void classifyPacket(const Pkt4Ptr& pkt);
1033 
1034 protected:
1035 
1036     /// @brief Assigns incoming packet to zero or more classes (required pass).
1037     ///
1038     /// @note This required classification evaluates all classes which
1039     /// were marked for required evaluation. Classes are collected so
1040     /// evaluated in the reversed order than output option processing.
1041     ///
1042     /// @note The only-if-required flag is related because it avoids
1043     /// double evaluation (which is not forbidden).
1044     ///
1045     /// @param ex The exchange holding needed information.
1046     void requiredClassify(Dhcpv4Exchange& ex);
1047 
1048     /// @brief Perform deferred option unpacking.
1049     ///
1050     /// @note Options 43 and 224-254 are processed after classification.
1051     /// If a class configures a definition it is applied, if none
1052     /// the global (user) definition is applied. For option 43
1053     /// a last resort definition (same definition as used in previous Kea
1054     /// versions) is applied when none is found.
1055     ///
1056     /// @param query Pointer to the client message.
1057     void deferredUnpack(Pkt4Ptr& query);
1058 
1059     /// @brief Executes pkt4_send callout.
1060     ///
1061     /// @param callout_handle pointer to the callout handle.
1062     /// @param query Pointer to a query.
1063     /// @param rsp Pointer to a response.
1064     void processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
1065                               Pkt4Ptr& query, Pkt4Ptr& rsp);
1066 
1067     /// @brief Executes buffer4_send callout and sends the response.
1068     ///
1069     /// @param callout_handle pointer to the callout handle.
1070     /// @param rsp pointer to a response.
1071     void processPacketBufferSend(hooks::CalloutHandlePtr& callout_handle,
1072                                  Pkt4Ptr& rsp);
1073 
1074 private:
1075 
1076     /// @public
1077     /// @brief Assign class using vendor-class-identifier option
1078     ///
1079     /// @note This is the first part of @ref classifyPacket
1080     ///
1081     /// @param pkt packet to be classified
1082     void classifyByVendor(const Pkt4Ptr& pkt);
1083 
1084     /// @private
1085     /// @brief Constructs netmask option based on subnet4
1086     /// @param subnet subnet for which the netmask will be calculated
1087     ///
1088     /// @return Option that contains netmask information
1089     static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet);
1090 
1091 protected:
1092 
1093     /// UDP port number on which server listens.
1094     uint16_t server_port_;
1095 
1096     /// UDP port number to which server sends all responses.
1097     uint16_t client_port_;
1098 
1099     /// Indicates if shutdown is in progress. Setting it to true will
1100     /// initiate server shutdown procedure.
1101     volatile bool shutdown_;
1102 
1103     /// @brief Allocation Engine.
1104     /// Pointer to the allocation engine that we are currently using
1105     /// It must be a pointer, because we will support changing engines
1106     /// during normal operation (e.g. to use different allocators)
1107     boost::shared_ptr<AllocEngine> alloc_engine_;
1108 
1109     /// Should broadcast be enabled on sockets (if true).
1110     bool use_bcast_;
1111 
1112     /// @brief Holds information about disabled DHCP service and/or
1113     /// disabled subnet/network scopes.
1114     NetworkStatePtr network_state_;
1115 
1116     /// @brief Controls access to the configuration backends.
1117     CBControlDHCPv4Ptr cb_control_;
1118 
1119 private:
1120 
1121     /// @brief store value that defines if kea will send responses
1122     /// to a source address of incoming packet. Only for testing.
1123     bool test_send_responses_to_source_;
1124 
1125 public:
1126 
1127     /// Class methods for DHCPv4-over-DHCPv6 handler
1128 
1129     /// @brief Updates statistics for received packets
1130     /// @param query packet received
1131     static void processStatsReceived(const Pkt4Ptr& query);
1132 
1133     /// @brief Updates statistics for transmitted packets
1134     /// @param response packet transmitted
1135     static void processStatsSent(const Pkt4Ptr& response);
1136 
1137     /// @brief Returns the index for "buffer4_receive" hook point
1138     /// @return the index for "buffer4_receive" hook point
1139     static int getHookIndexBuffer4Receive();
1140 
1141     /// @brief Returns the index for "pkt4_receive" hook point
1142     /// @return the index for "pkt4_receive" hook point
1143     static int getHookIndexPkt4Receive();
1144 
1145     /// @brief Returns the index for "subnet4_select" hook point
1146     /// @return the index for "subnet4_select" hook point
1147     static int getHookIndexSubnet4Select();
1148 
1149     /// @brief Returns the index for "lease4_release" hook point
1150     /// @return the index for "lease4_release" hook point
1151     static int getHookIndexLease4Release();
1152 
1153     /// @brief Returns the index for "pkt4_send" hook point
1154     /// @return the index for "pkt4_send" hook point
1155     static int getHookIndexPkt4Send();
1156 
1157     /// @brief Returns the index for "buffer4_send" hook point
1158     /// @return the index for "buffer4_send" hook point
1159     static int getHookIndexBuffer4Send();
1160 
1161     /// @brief Returns the index for "lease4_decline" hook point
1162     /// @return the index for "lease4_decline" hook point
1163     static int getHookIndexLease4Decline();
1164 
1165     /// @brief Return a list of all paths that contain passwords or secrets for
1166     /// kea-dhcp4.
1167     ///
1168     /// @return the list of lists of sequential JSON map keys needed to reach
1169     /// the passwords and secrets.
1170     std::list<std::list<std::string>> jsonPathsToRedact() const final override;
1171 };
1172 
1173 }  // namespace dhcp
1174 }  // namespace isc
1175 
1176 #endif // DHCP4_SRV_H
1177