dhcp4_srv.h 39.8 KB
Newer Older
1
// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 7 8 9

#ifndef DHCPV4_SRV_H
#define DHCPV4_SRV_H

10
#include <asiolink/io_service.h>
11
#include <dhcp/dhcp4.h>
12 13
#include <dhcp/pkt4.h>
#include <dhcp/option.h>
14
#include <dhcp/option_string.h>
15
#include <dhcp/option4_client_fqdn.h>
16
#include <dhcp/option_custom.h>
17
#include <dhcp_ddns/ncr_msg.h>
18
#include <dhcpsrv/alloc_engine.h>
19
#include <dhcpsrv/cfg_option.h>
20
#include <dhcpsrv/callout_handle_store.h>
21 22 23
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/network_state.h>
#include <dhcpsrv/subnet.h>
24
#include <hooks/callout_handle.h>
25
#include <dhcpsrv/daemon.h>
26 27 28

#include <boost/noncopyable.hpp>

29
#include <functional>
30
#include <iostream>
31
#include <queue>
32

33 34 35 36
// Undefine the macro OPTIONAL which is defined in some operating
// systems but conflicts with a member of the RequirementLevel enum in
// the server class.

37 38 39 40
#ifdef OPTIONAL
#undef OPTIONAL
#endif

41 42
namespace isc {
namespace dhcp {
43

44 45 46 47 48
/// @brief DHCPv4 message exchange.
///
/// This class represents the DHCPv4 message exchange. The message exchange
/// consists of the single client message, server response to this message
/// and the mechanisms to generate the server's response. The server creates
49
/// the instance of the @c Dhcpv4Exchange for each inbound message that it
50 51
/// accepts for processing.
///
52
/// The use of the @c Dhcpv4Exchange object as a central repository of
53 54 55 56 57 58 59 60 61 62
/// information about the message exchange simplifies the API of the
/// @c Dhcpv4Srv class.
///
/// Another benefit of using this class is that different methods of the
/// @c Dhcpv4Srv may share information. For example, the constructor of this
/// class selects the subnet and multiple methods of @c Dhcpv4Srv use this
/// subnet, without the need to select it again.
///
/// @todo This is the initial version of this class. In the future a lot of
/// code from the @c Dhcpv4Srv class will be migrated here.
63
class Dhcpv4Exchange {
64 65 66 67 68 69 70 71 72 73 74 75
public:
    /// @brief Constructor.
    ///
    /// The constructor selects the subnet for the query and checks for the
    /// static host reservations for the client which has sent the message.
    /// The information about the reservations is stored in the
    /// @c AllocEngine::ClientContext4 object, which can be obtained by
    /// calling the @c getContext.
    ///
    /// @param alloc_engine Pointer to the instance of the Allocation Engine
    /// used by the server.
    /// @param query Pointer to the client message.
76 77 78
    /// @param subnet Pointer to the subnet to which the client belongs.
    Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, const Pkt4Ptr& query,
                   const Subnet4Ptr& subnet);
79 80 81 82 83 84 85 86 87

    /// @brief Initializes the instance of the response message.
    ///
    /// The type of the response depends on the type of the query message.
    /// For the DHCPDISCOVER the DHCPOFFER is created. For the DHCPREQUEST
    /// and DHCPINFORM the DHCPACK is created. For the DHCPRELEASE the
    /// response is not initialized.
    void initResponse();

88 89 90 91 92
    /// @brief Initializes the DHCPv6 part of the response message
    ///
    /// Called by initResponse() when the query is a DHCP4o6 message
    void initResponse4o6();

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
    /// @brief Returns the pointer to the query from the client.
    Pkt4Ptr getQuery() const {
        return (query_);
    }

    /// @brief Returns the pointer to the server's response.
    ///
    /// The returned pointer is NULL if the query type is DHCPRELEASE or DHCPDECLINE.
    Pkt4Ptr getResponse() const {
        return (resp_);
    }

    /// @brief Removes the response message by resetting the pointer to NULL.
    void deleteResponse() {
        resp_.reset();
    }

    /// @brief Returns the copy of the context for the Allocation engine.
    AllocEngine::ClientContext4Ptr getContext() const {
        return (context_);
    }

115
    /// @brief Returns the configured option list (non-const version)
116 117 118 119
    CfgOptionList& getCfgOptionList() {
        return (cfg_option_list_);
    }

120
    /// @brief Returns the configured option list (const version)
121 122 123 124
    const CfgOptionList& getCfgOptionList() const {
        return (cfg_option_list_);
    }

125 126 127 128
    /// @brief Sets reserved values of siaddr, sname and file in the
    /// server's response.
    void setReservedMessageFields();

129 130 131
    /// @brief Assigns classes retrieved from host reservation database.
    void setReservedClientClasses();

132
private:
133 134 135 136 137 138 139 140 141 142 143 144

    /// @brief Copies default parameters from client's to server's message
    ///
    /// Some fields are copied from client's message into server's response,
    /// e.g. client HW address, number of hops, transaction-id etc.
    ///
    /// @warning This message is called internally by @c initResponse and
    /// thus it doesn't check if the resp_ value has been initialized. The
    /// calling method is responsible for making sure that @c resp_ is
    /// not NULL.
    void copyDefaultFields();

145 146 147 148 149 150 151 152 153 154 155
    /// @brief Copies default options from client's to server's message
    ///
    /// Some options are copied from client's message into server's response,
    /// e.g. Relay Agent Info option, Subnet Selection option etc.
    ///
    /// @warning This message is called internally by @c initResponse and
    /// thus it doesn't check if the resp_ value has been initialized. The
    /// calling method is responsible for making sure that @c resp_ is
    /// not NULL.
    void copyDefaultOptions();

156 157
    /// @brief Set host identifiers within a context.
    ///
158
    /// This method sets an ordered list of host identifier types and
159
    /// values which the server should use to find host reservations.
160 161
    /// The order of the set is determined by the configuration parameter,
    /// host-reservation-identifiers
162 163
    void setHostIdentifiers();

164 165 166 167 168 169 170 171
    /// @brief Pointer to the allocation engine used by the server.
    AllocEnginePtr alloc_engine_;
    /// @brief Pointer to the DHCPv4 message sent by the client.
    Pkt4Ptr query_;
    /// @brief Pointer to the DHCPv4 message to be sent to the client.
    Pkt4Ptr resp_;
    /// @brief Context for use with allocation engine.
    AllocEngine::ClientContext4Ptr context_;
172 173 174
    /// @brief Configured option list.
    /// @note The configured option list is an *ordered* list of
    /// @c CfgOption objects used to append options to the response.
175
    CfgOptionList cfg_option_list_;
176 177
};

178 179
/// @brief Type representing the pointer to the @c Dhcpv4Exchange.
typedef boost::shared_ptr<Dhcpv4Exchange> Dhcpv4ExchangePtr;
180 181


182 183 184 185 186 187 188 189
/// @brief DHCPv4 server service.
///
/// This singleton class represents DHCPv4 server. It contains all
/// top-level methods and routines necessary for server operation.
/// In particular, it instantiates IfaceMgr, loads or generates DUID
/// that is going to be used as server-identifier, receives incoming
/// packets, processes them, manages leases assignment and generates
/// appropriate responses.
190 191
///
/// This class does not support any controlling mechanisms directly.
192
/// See the derived \ref ControlledDhcpv4Srv class for support for
193
/// command and configuration updates over msgq.
194
class Dhcpv4Srv : public Daemon {
195 196 197 198
private:

    /// @brief Pointer to IO service used by the server.
    asiolink::IOServicePtr io_service_;
199

200
public:
201 202 203 204 205 206 207 208

    /// @brief defines if certain option may, must or must not appear
    typedef enum {
        FORBIDDEN,
        MANDATORY,
        OPTIONAL
    } RequirementLevel;

209 210
    /// @brief Default constructor.
    ///
211
    /// Instantiates necessary services, required to run DHCPv4 server.
212 213
    /// In particular, creates IfaceMgr that will be responsible for
    /// network interaction. Will instantiate lease manager, and load
Tomek Mrugalski's avatar
Tomek Mrugalski committed
214 215
    /// old or create new DUID. It is possible to specify alternate
    /// port on which DHCPv4 server will listen on. That is mostly useful
216 217
    /// for testing purposes. The Last two arguments of the constructor
    /// should be left at default values for normal server operation.
Marcin Siodelski's avatar
Marcin Siodelski committed
218 219 220
    /// They should be set to 'false' when creating an instance of this
    /// class for unit testing because features they enable require
    /// root privileges.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
221 222
    ///
    /// @param port specifies port number to listen on
223
    /// @param use_bcast configure sockets to support broadcast messages.
224 225
    /// @param direct_response_desired specifies if it is desired to
    /// use direct V4 traffic.
226
    Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT,
227 228
              const bool use_bcast = true,
              const bool direct_response_desired = true);
229

230
    /// @brief Destructor. Used during DHCPv4 service shutdown.
231
    virtual ~Dhcpv4Srv();
232

233 234 235 236 237
    /// @brief Returns pointer to the IO service used by the server.
    asiolink::IOServicePtr& getIOService() {
        return (io_service_);
    }

238 239 240 241 242
    /// @brief Returns pointer to the network state used by the server.
    NetworkStatePtr& getNetworkState() {
        return (network_state_);
    }

243
    /// @brief returns Kea version on stdout and exit.
244
    /// redeclaration/redefinition. @ref Daemon::getVersion()
245
    static std::string getVersion(bool extended);
Andrei Pavel's avatar
Andrei Pavel committed
246

247 248
    /// @brief Main server processing loop.
    ///
Francis Dupont's avatar
Francis Dupont committed
249
    /// Main server processing loop. Call the processing step routine
Francis Dupont's avatar
Francis Dupont committed
250
    /// until shut down.
251
    ///
Francis Dupont's avatar
Francis Dupont committed
252
    /// @return true, if being shut down gracefully, never fail.
253 254
    bool run();

Francis Dupont's avatar
Francis Dupont committed
255
    /// @brief Main server processing step.
Francis Dupont's avatar
Francis Dupont committed
256
    ///
Francis Dupont's avatar
Francis Dupont committed
257
    /// Main server processing step. Receives one incoming packet, calls
258 259
    /// the processing packet routing and (if necessary) transmits
    /// a response.
Francis Dupont's avatar
Francis Dupont committed
260 261
    void run_one();

262 263 264
    /// @brief Process a single incoming DHCPv4 packet.
    ///
    /// It verifies correctness of the passed packet, call per-type processXXX
265
    /// methods, generates appropriate answer.
266 267
    ///
    /// @param query A pointer to the packet to be processed.
268
    /// @param rsp A pointer to the response
269 270 271
    /// @param allow_packet_park Indicates if parking a packet is allowed.
    void processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp,
                       bool allow_packet_park = true);
272 273


274
    /// @brief Instructs the server to shut down.
275 276
    void shutdown();

277 278 279 280 281 282 283
    ///
    /// @name Public accessors returning values required to (re)open sockets.
    ///
    //@{
    ///
    /// @brief Get UDP port on which server should listen.
    ///
284 285 286
    /// Typically, server listens on UDP port number 67. Other ports are used
    /// for testing purposes only.
    ///
287 288 289 290 291 292 293 294 295 296 297 298 299 300
    /// @return UDP port on which server should listen.
    uint16_t getPort() const {
        return (port_);
    }

    /// @brief Return bool value indicating that broadcast flags should be set
    /// on sockets.
    ///
    /// @return A bool value indicating that broadcast should be used (if true).
    bool useBroadcast() const {
        return (use_bcast_);
    }
    //@}

301 302
    /// @brief Starts DHCP_DDNS client IO if DDNS updates are enabled.
    ///
303
    /// If updates are enabled, it instructs the D2ClientMgr singleton to
304
    /// enter send mode.  If D2ClientMgr encounters errors it may throw
305
    /// D2ClientError. This method does not catch exceptions.
306 307
    void startD2();

308 309 310 311
    /// @brief Stops DHCP_DDNS client IO if DDNS updates are enabled.
    ///
    /// If updates are enabled, it instructs the D2ClientMgr singleton to
    /// leave send mode.  If D2ClientMgr encounters errors it may throw
312
    /// D2ClientError. This method does not catch exceptions.
313 314
    void stopD2();

315 316
    /// @brief Implements the error handler for DHCP_DDNS IO errors
    ///
317
    /// Invoked when a NameChangeRequest send to kea-dhcp-ddns completes with
318 319 320 321 322 323 324 325 326 327 328 329 330 331
    /// a failed status.  These are communications errors, not data related
    /// failures.
    ///
    /// This method logs the failure and then suspends all further updates.
    /// Updating can only be restored by reconfiguration or restarting the
    /// server.  There is currently no retry logic so the first IO error that
    /// occurs will suspend updates.
    /// @todo We may wish to make this more robust or sophisticated.
    ///
    /// @param result Result code of the send operation.
    /// @param ncr NameChangeRequest which failed to send.
    virtual void d2ClientErrorHandler(const dhcp_ddns::
                                      NameChangeSender::Result result,
                                      dhcp_ddns::NameChangeRequestPtr& ncr);
332
protected:
333

334 335 336 337 338 339 340
    /// @name Functions filtering and sanity-checking received messages.
    ///
    /// @todo These functions are supposed to be moved to a new class which
    /// will manage different rules for accepting and rejecting messages.
    /// Perhaps ticket #3116 is a good opportunity to do it.
    ///
    //@{
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
    /// @brief Checks whether received message should be processed or discarded.
    ///
    /// This function checks whether received message should be processed or
    /// discarded. It should be called on the beginning of message processing
    /// (just after the message has been decoded). This message calls a number
    /// of other functions which check whether message should be processed,
    /// using different criteria.
    ///
    /// This function should be extended when new criteria for accepting
    /// received message have to be implemented. This function is meant to
    /// aggregate all early filtering checks on the received message. By having
    /// a single function like this, we are avoiding bloat of the server's main
    /// loop.
    ///
    /// @warning This function should remain exception safe.
    ///
    /// @param query Received message.
    ///
    /// @return true if the message should be further processed, or false if
    /// the message should be discarded.
    bool accept(const Pkt4Ptr& query) const;

    /// @brief Check if a message sent by directly connected client should be
Francis Dupont's avatar
Francis Dupont committed
364
    /// accepted or discarded.
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    ///
    /// This function checks if the received message is from directly connected
    /// client. If it is, it checks that it should be processed or discarded.
    ///
    /// Note that this function doesn't validate all addresses being carried in
    /// the message. The primary purpose of this function is to filter out
    /// direct messages in the local network for which there is no suitable
    /// subnet configured. For example, this function accepts unicast messages
    /// because unicasts may be used by clients located in remote networks to
    /// to renew existing leases. If their notion of address is wrong, the
    /// server will have to sent a NAK, instead of dropping the message.
    /// Detailed validation of such messages is performed at later stage of
    /// processing.
    ///
    /// This function accepts the following messages:
    /// - all valid relayed messages,
    /// - all unicast messages,
382 383 384
    /// - all broadcast messages except DHCPINFORM received on the interface
    /// for which the suitable subnet exists (is configured).
    /// - all DHCPINFORM messages with source address or ciaddr set.
385
    ///
386
    /// @param query Message sent by a client.
387 388 389
    ///
    /// @return true if message is accepted for further processing, false
    /// otherwise.
390 391 392 393 394
    bool acceptDirectRequest(const Pkt4Ptr& query) const;

    /// @brief Check if received message type is valid for the server to
    /// process.
    ///
Francis Dupont's avatar
Francis Dupont committed
395 396 397
    /// This function checks that the received message type belongs to
    /// the range of types recognized by the server and that the
    /// message of this type should be processed by the server.
398 399 400 401 402 403 404 405 406 407 408 409 410
    ///
    /// The messages types accepted for processing are:
    /// - Discover
    /// - Request
    /// - Release
    /// - Decline
    /// - Inform
    ///
    /// @param query Message sent by a client.
    ///
    /// @return true if message is accepted for further processing, false
    /// otherwise.
    bool acceptMessageType(const Pkt4Ptr& query) const;
411

412 413 414 415 416 417 418 419 420 421 422 423 424 425
    /// @brief Verifies if the server id belongs to our server.
    ///
    /// This function checks if the server identifier carried in the specified
    /// DHCPv4 message belongs to this server. If the server identifier option
    /// is absent or the value carried by this option is equal to one of the
    /// server identifiers used by the server, the true is returned. If the
    /// server identifier option is present, but it doesn't match any server
    /// identifier used by this server, the false value is returned.
    ///
    /// @param pkt DHCPv4 message which server identifier is to be checked.
    ///
    /// @return true, if the server identifier is absent or matches one of the
    /// server identifiers that the server is using; false otherwise.
    bool acceptServerId(const Pkt4Ptr& pkt) const;
426
    //@}
427

428
    /// @brief Verifies if specified packet meets RFC requirements
429 430 431 432
    ///
    /// Checks if mandatory option is really there, that forbidden option
    /// is not there, and that client-id or server-id appears only once.
    ///
433
    /// @param query Pointer to the client's message.
434 435
    /// @param serverid expectation regarding server-id option
    /// @throw RFCViolation if any issues are detected
436
    static void sanityCheck(const Pkt4Ptr& query, RequirementLevel serverid);
437

438 439 440 441 442 443
    /// @brief Processes incoming DISCOVER and returns response.
    ///
    /// Processes received DISCOVER message and verifies that its sender
    /// should be served. In particular, a lease is selected and sent
    /// as an offer to a client if it should be served.
    ///
444
    /// @param discover DISCOVER message received from client
445 446
    ///
    /// @return OFFER message or NULL
447
    Pkt4Ptr processDiscover(Pkt4Ptr& discover);
448 449 450 451 452

    /// @brief Processes incoming REQUEST and returns REPLY response.
    ///
    /// Processes incoming REQUEST message and verifies that its sender
    /// should be served. In particular, verifies that requested lease
Tomek Mrugalski's avatar
Tomek Mrugalski committed
453
    /// is valid, not expired, not reserved, not used by other client and
454 455
    /// that requesting client is allowed to use it.
    ///
456
    /// Returns ACK message, NAK message, or NULL
457 458
    ///
    /// @param request a message received from client
459 460
    /// @param [out] context pointer to the client context where allocated
    /// and deleted leases are stored.
461
    ///
462
    /// @return ACK or NAK message
463
    Pkt4Ptr processRequest(Pkt4Ptr& request, AllocEngine::ClientContext4Ptr& context);
464

465
    /// @brief Processes incoming DHCPRELEASE messages.
466 467 468 469 470
    ///
    /// In DHCPv4, server does not respond to RELEASE messages, therefore
    /// this function does not return anything.
    ///
    /// @param release message received from client
471 472 473
    /// @param [out] context pointer to the client context where released
    /// lease is stored.
    void processRelease(Pkt4Ptr& release, AllocEngine::ClientContext4Ptr& context);
474

Tomek Mrugalski's avatar
Tomek Mrugalski committed
475 476 477
    /// @brief Process incoming DHCPDECLINE messages.
    ///
    /// This method processes incoming DHCPDECLINE. In particular, it extracts
Shawn Routhier's avatar
Shawn Routhier committed
478
    /// Requested IP Address option, checks that the address really belongs to
Tomek Mrugalski's avatar
Tomek Mrugalski committed
479
    /// the client and if it does, calls @ref declineLease.
480 481
    ///
    /// @param decline message received from client
482 483 484
    /// @param [out] context pointer to the client context where declined
    /// lease is stored.
    void processDecline(Pkt4Ptr& decline, AllocEngine::ClientContext4Ptr& context);
485

486
    /// @brief Processes incoming DHCPINFORM messages.
487
    ///
488
    /// @param inform message received from client
489 490
    ///
    /// @return DHCPACK to be sent to the client.
491
    Pkt4Ptr processInform(Pkt4Ptr& inform);
492

493 494 495 496 497 498 499 500
    /// @brief Build the configured option list
    ///
    /// @note The configured option list is an *ordered* list of
    /// @c CfgOption objects used to append options to the response.
    ///
    /// @param ex The exchange where the configured option list is cached
    void buildCfgOptionList(Dhcpv4Exchange& ex);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
501 502 503
    /// @brief Appends options requested by client.
    ///
    /// This method assigns options that were requested by client
Tomek Mrugalski's avatar
Tomek Mrugalski committed
504
    /// (sent in PRL) or are enforced by server.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
505
    ///
506 507
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
508
    void appendRequestedOptions(Dhcpv4Exchange& ex);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
509

510 511 512 513 514 515 516 517
    /// @brief Appends requested vendor options as requested by client.
    ///
    /// This method is similar to \ref appendRequestedOptions(), but uses
    /// vendor options. The major difference is that vendor-options use
    /// its own option spaces (there may be more than one distinct set of vendor
    /// options, each with unique vendor-id). Vendor options are requested
    /// using separate options within their respective vendor-option spaces.
    ///
518 519
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
520
    void appendRequestedVendorOptions(Dhcpv4Exchange& ex);
521

Tomek Mrugalski's avatar
Tomek Mrugalski committed
522 523
    /// @brief Assigns a lease and appends corresponding options
    ///
Francis Dupont's avatar
Francis Dupont committed
524
    /// This method chooses the most appropriate lease for requesting
Tomek Mrugalski's avatar
Tomek Mrugalski committed
525 526 527
    /// client and assigning it. Options corresponding to the lease
    /// are added to specific message.
    ///
528 529 530 531 532 533 534 535
    /// This method may reset the pointer to the response in the @c ex object
    /// to indicate that the response should not be sent to the client.
    /// The caller must check if the response is is null after calling
    /// this method.
    ///
    /// The response type in the @c ex object may be set to DHCPACK or DHCPNAK.
    ///
    /// @param ex DHCPv4 exchange holding the client's message to be checked.
536
    void assignLease(Dhcpv4Exchange& ex);
537

538 539 540
    /// @brief Append basic options if they are not present.
    ///
    /// This function adds the following basic options if they
541
    /// are not yet added to the response message:
542 543 544
    /// - Subnet Mask,
    /// - Router,
    /// - Name Server,
545 546
    /// - Domain Name,
    /// - Server Identifier.
547
    ///
548
    /// @param ex DHCPv4 exchange holding the client's message to be checked.
549
    void appendBasicOptions(Dhcpv4Exchange& ex);
550

551 552 553 554
    /// @brief Sets fixed fields of the outgoing packet.
    ///
    /// If the incoming packets belongs to a class and that class defines
    /// next-server, server-hostname or boot-file-name, we need to set the
Tomek Mrugalski's avatar
Tomek Mrugalski committed
555 556 557 558
    /// siaddr, sname or filename fields in the outgoing packet. Also, those
    /// values can be defined for subnet or in reservations. The values
    /// defined in reservation takes precedence over class values, which
    /// in turn take precedence over subnet values.
559 560 561 562 563
    ///
    /// @param ex DHCPv4 exchange holding the client's message and the server's
    ///           response to be adjusted.
    void setFixedFields(Dhcpv4Exchange& ex);

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
    /// @brief Processes Client FQDN and Hostname Options sent by a client.
    ///
    /// Client may send Client FQDN or Hostname option to communicate its name
    /// to the server. Server may use this name to perform DNS update for the
    /// lease being assigned to a client. If server takes responsibility for
    /// updating DNS for a client it may communicate it by sending the Client
    /// FQDN or Hostname %Option back to the client. Server select a different
    /// name than requested by a client to update DNS. In such case, the server
    /// stores this different name in its response.
    ///
    /// Client should not send both Client FQDN and Hostname options. However,
    /// if client sends both options, server should prefer Client FQDN option
    /// and ignore the Hostname option. If Client FQDN option is not present,
    /// the Hostname option is processed.
    ///
    /// The Client FQDN %Option is processed by this function as described in
    /// RFC4702.
    ///
    /// In response to a Hostname %Option sent by a client, the server may send
    /// Hostname option with the same or different hostname. If different
    /// hostname is sent, it is an indication to the client that server has
    /// overridden the client's preferred name and will rather use this
    /// different name to update DNS. However, since Hostname option doesn't
    /// carry an information whether DNS update will be carried by the server
    /// or not, the client is responsible for checking whether DNS update
    /// has been performed.
    ///
    /// After successful processing options stored in the first parameter,
    /// this function may add Client FQDN or Hostname option to the response
    /// message. In some cases, server may cease to add any options to the
    /// response, i.e. when server doesn't support DNS updates.
    ///
596 597 598
    /// This function does not throw. It simply logs the debug message if the
    /// processing of the FQDN or Hostname failed.
    ///
599 600
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
601
    void processClientName(Dhcpv4Exchange& ex);
602

Josh Soref's avatar
Josh Soref committed
603
    /// @brief this is a prefix added to the content of vendor-class option
604 605 606 607 608 609 610
    ///
    /// If incoming packet has a vendor class option, its content is
    /// prepended with this prefix and then interpreted as a class.
    /// For example, a packet that sends vendor class with value of "FOO"
    /// will cause the packet to be assigned to class VENDOR_CLASS_FOO.
    static const std::string VENDOR_CLASS_PREFIX;

611 612 613
private:
    /// @brief Process Client FQDN %Option sent by a client.
    ///
614 615 616 617 618 619
    /// This function is called by the @c Dhcpv4Srv::processClientName when
    /// the client has sent the FQDN option in its message to the server.
    /// It comprises the actual logic to parse the FQDN option and prepare
    /// the FQDN option to be sent back to the client in the server's
    /// response.
    ///
620 621
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
622
    void processClientFqdnOption(Dhcpv4Exchange& ex);
623 624 625

    /// @brief Process Hostname %Option sent by a client.
    ///
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
    /// This method is called by the @c Dhcpv4Srv::processClientName to
    /// create an instance of the Hostname option to be returned to the
    /// client. If this instance is created it is included in the response
    /// message within the @c Dhcpv4Exchange object passed as an argument.
    ///
    /// The Hostname option instance is created if the client has included
    /// Hostname option in its query to the server or if the client has
    /// included Hostname option code in the Parameter Request List option.
    /// In the former case, the server can use the Hostname supplied by the
    /// client or replace it with a new hostname, depending on the server's
    /// configuration. A reserved hostname takes precedence over a hostname
    /// supplied by the client or auto generated hostname.
    ///
    /// If the 'qualifying-suffix' parameter is specified, its value is used
    /// to qualify a hostname. For example, if the host reservation contains
    /// a hostname 'marcin-laptop', and the qualifying suffix is
    /// 'example.isc.org', the hostname returned to the client will be
    /// 'marcin-laptop.example.isc.org'. If the 'qualifying-suffix' is not
    /// specified (empty), the reserved hostname is returned to the client
    /// unqualified.
    ///
    /// The 'qualifying-suffix' value is also used to qualify the hostname
    /// supplied by the client, when this hostname is unqualified,
    /// e.g. 'laptop-x'. If the supplied hostname is qualified, e.g.
    /// 'laptop-x.example.org', the qualifying suffix will not be appended
    /// to it.
652
    ///
653 654
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
655
    void processHostnameOption(Dhcpv4Exchange& ex);
656

Francis Dupont's avatar
Francis Dupont committed
657
    /// @public
658 659 660 661 662 663 664
    /// @brief Marks lease as declined.
    ///
    /// This method moves a lease to declined state with all the steps involved:
    /// - trigger DNS removal (if necessary)
    /// - disassociate the client information
    /// - update lease in the database (switch to DECLINED state)
    /// - increase necessary statistics
665
    /// - call lease4_decline hook
666 667
    ///
    /// @param lease lease to be declined
668
    /// @param decline client's message
669
    /// @param context reference to a client context
670 671
    void declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline,
                      AllocEngine::ClientContext4Ptr& context);
672

673
protected:
674 675 676 677

    /// @brief Creates NameChangeRequests which correspond to the lease
    /// which has been acquired.
    ///
Francis Dupont's avatar
Francis Dupont committed
678
    /// If this function is called when an existing lease is renewed, it
679 680 681 682 683 684 685 686 687 688 689 690 691
    /// may generate NameChangeRequest to remove existing DNS entries which
    /// correspond to the old lease instance. This function may cease to
    /// generate NameChangeRequests if the notion of the client's FQDN hasn't
    /// changed between an old and new lease.
    ///
    /// @param lease A pointer to the new lease which has been acquired.
    /// @param old_lease A pointer to the instance of the old lease which has
    /// been replaced by the new lease passed in the first argument. The NULL
    /// value indicates that the new lease has been allocated, rather than
    /// lease being renewed.
    void createNameChangeRequests(const Lease4Ptr& lease,
                                  const Lease4Ptr& old_lease);

692 693 694 695 696
    /// @brief Attempts to renew received addresses
    ///
    /// Attempts to renew existing lease. This typically includes finding a lease that
    /// corresponds to the received address. If no such lease is found, a status code
    /// response is generated.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
697
    ///
698 699 700
    /// @param renew client's message asking for renew
    /// @param reply server's response (ACK or NAK)
    void renewLease(const Pkt4Ptr& renew, Pkt4Ptr& reply);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
701

702 703
    /// @brief Adds server identifier option to the server's response.
    ///
704 705 706 707 708
    /// This method adds a server identifier to the DHCPv4 message if it doesn't
    /// exist yet. This is set to the local address on which the client's query has
    /// been received with the exception of broadcast traffic and DHCPv4o6 query for
    /// which a socket on the particular interface is found and its address is used
    /// as server id.
709 710 711 712 713 714
    ///
    /// @note This method doesn't throw exceptions by itself but the underlying
    /// classes being used my throw. The reason for this method to not sanity
    /// check the specified message is that it is meant to be called internally
    /// by the @c Dhcpv4Srv class.
    ///
715 716 717
    /// @note This method is static because it is not dependent on the class
    /// state.
    ///
718 719
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
720
    static void appendServerID(Dhcpv4Exchange& ex);
721

722 723 724 725 726 727
    /// @brief Check if the relay port RAI sub-option was set in the query.
    ///
    /// @param ex The exchange holding the client's message
    /// @return the port to use to join the relay or 0 for the default
    static uint16_t checkRelayPort(const Dhcpv4Exchange& ex);

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
    /// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
    ///
    /// This method sets the following parameters for the DHCPv4 message being
    /// sent to a client:
    /// - client unicast or a broadcast address,
    /// - client or relay port,
    /// - server address,
    /// - server port,
    /// - name and index of the interface which is to be used to send the
    /// message.
    ///
    /// Internally it calls the @c Dhcpv4Srv::adjustRemoteAddr to figure
    /// out the destination address (client unicast address or broadcast
    /// address).
    ///
    /// The destination port is always DHCPv4 client (68) or relay (67) port,
744
    /// depending if the response will be sent directly to a client.
745 746 747 748 749 750 751 752 753 754
    ///
    /// The source port is always set to DHCPv4 server port (67).
    ///
    /// The interface selected for the response is always the same as the
    /// one through which the query has been received.
    ///
    /// The source address for the response is the IPv4 address assigned to
    /// the interface being used to send the response. This function uses
    /// @c IfaceMgr to get the socket bound to the IPv4 address on the
    /// particular interface.
755 756 757
    ///
    /// @note This method is static because it is not dependent on the class
    /// state.
758 759 760
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
761
    static void adjustIfaceData(Dhcpv4Exchange& ex);
762

763 764 765 766 767 768 769 770 771 772 773 774 775
    /// @brief Sets remote addresses for outgoing packet.
    ///
    /// This method sets the local and remote addresses on outgoing packet.
    /// The addresses being set depend on the following conditions:
    /// - has incoming packet been relayed,
    /// - is direct response to a client without address supported,
    /// - type of the outgoing packet,
    /// - broadcast flag set in the incoming packet.
    ///
    /// @warning This method does not check whether provided packet pointers
    /// are valid. Make sure that pointers are correct before calling this
    /// function.
    ///
776 777 778
    /// @note This method is static because it is not dependent on the class
    /// state.
    ///
779 780
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
781
    static void adjustRemoteAddr(Dhcpv4Exchange& ex);
782

783 784 785 786 787 788 789
    /// @brief converts server-id to text
    /// Converts content of server-id option to a text representation, e.g.
    /// "192.0.2.1"
    ///
    /// @param opt option that contains server-id
    /// @return string representation
    static std::string srvidToString(const OptionPtr& opt);
790

791 792
    /// @brief Selects a subnet for a given client's packet.
    ///
793 794 795 796 797
    /// If selectSubnet is called to simply do sanity checks (check if a
    /// subnet would be selected), then there is no need to call hooks,
    /// as this will happen later (when selectSubnet is called again).
    /// In such case the sanity_only should be set to true.
    ///
798
    /// @param query client's message
799
    /// @param drop if it is true the packet will be dropped
800
    /// @param sanity_only if it is true the callout won't be called
801
    /// @return selected subnet (or NULL if no suitable subnet was found)
802
    isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& query,
803 804
                                       bool& drop,
                                       bool sanity_only = false) const;
805

806 807
    /// @brief Selects a subnet for a given client's DHCP4o6 packet.
    ///
808 809 810 811 812
    /// If selectSubnet is called to simply do sanity checks (check if a
    /// subnet would be selected), then there is no need to call hooks,
    /// as this will happen later (when selectSubnet is called again).
    /// In such case the sanity_only should be set to true.
    ///
813
    /// @param query client's message
814
    /// @param drop if it is true the packet will be dropped
815
    /// @param sanity_only if it is true the callout won't be called
816
    /// @return selected subnet (or NULL if no suitable subnet was found)
817
    isc::dhcp::Subnet4Ptr selectSubnet4o6(const Pkt4Ptr& query,
818 819
                                          bool& drop,
                                          bool sanity_only = false) const;
820

821 822
    /// indicates if shutdown is in progress. Setting it to true will
    /// initiate server shutdown procedure.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
823
    volatile bool shutdown_;
824

825 826 827 828 829 830 831 832 833 834 835 836
    /// @brief dummy wrapper around IfaceMgr::receive4
    ///
    /// This method is useful for testing purposes, where its replacement
    /// simulates reception of a packet. For that purpose it is protected.
    virtual Pkt4Ptr receivePacket(int timeout);

    /// @brief dummy wrapper around IfaceMgr::send()
    ///
    /// This method is useful for testing purposes, where its replacement
    /// simulates transmission of a packet. For that purpose it is protected.
    virtual void sendPacket(const Pkt4Ptr& pkt);

837 838
    /// @brief Assigns incoming packet to zero or more classes.
    ///
839
    /// @note This is done in two phases: first the content of the
840 841
    /// vendor-class-identifier option is used as a class, by
    /// calling @ref classifyByVendor(). Second classification match
842 843
    /// expressions are evaluated. The resulting classes will be stored
    /// in the packet (see @ref isc::dhcp::Pkt4::classes_ and
844
    /// @ref isc::dhcp::Pkt4::inClass).
845
    ///
846 847 848
    /// @param pkt packet to be classified
    void classifyPacket(const Pkt4Ptr& pkt);

849
    /// @brief Assigns incoming packet to zero or more classes (required pass).
850
    ///
851 852 853
    /// @note This required classification evaluates all classes which
    /// were marked for required evaluation. Classes are collected so
    /// evaluated in the reversed order than output option processing.
854
    ///
855
    /// @note The only-if-required flag is related because it avoids
856 857 858
    /// double evaluation (which is not forbidden).
    ///
    /// @param ex The exchange holding needed informations.
859
    void requiredClassify(Dhcpv4Exchange& ex);
860

861 862 863 864 865
    /// @brief Perform deferred option unpacking.
    ///
    /// @note Options 43 and 224-254 are processed after classification.
    /// If a class configures a definition it is applied, if none
    /// the global (user) definition is applied. For option 43
866 867 868 869
    /// a last resort definition (same definition as used in previous Kea
    /// versions) is applied when none is found.
    ///
    /// @param query Pointer to the client message.
870 871
    void deferredUnpack(Pkt4Ptr& query);

872 873 874 875 876 877 878 879 880 881 882
    /// @brief Executes pkt4_send callout.
    ///
    /// @param callout_handle pointer to the callout handle.
    /// @param query Pointer to a query.
    /// @param rsp Pointer to a response.
    void processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
                              Pkt4Ptr& query, Pkt4Ptr& rsp);

    /// @brief Executes buffer4_send callout and sends the response.
    ///
    /// @param callout_handle pointer to the callout handle.
883
    /// @param rsp pointer to a response.
884 885 886
    void processPacketBufferSend(hooks::CalloutHandlePtr& callout_handle,
                                 Pkt4Ptr& rsp);

887 888 889 890 891
    /// @brief Allocation Engine.
    /// Pointer to the allocation engine that we are currently using
    /// It must be a pointer, because we will support changing engines
    /// during normal operation (e.g. to use different allocators)
    boost::shared_ptr<AllocEngine> alloc_engine_;
892

893
private:
894

Francis Dupont's avatar
Francis Dupont committed
895
    /// @public
896 897 898 899 900
    /// @brief Assign class using vendor-class-identifier option
    ///
    /// @note This is the first part of @ref classifyPacket
    ///
    /// @param pkt packet to be classified
901
    void classifyByVendor(const Pkt4Ptr& pkt);
902

Francis Dupont's avatar
Francis Dupont committed
903
    /// @private
904
    /// @brief Constructs netmask option based on subnet4
905
    /// @param subnet subnet for which the netmask will be calculated
906 907 908 909
    ///
    /// @return Option that contains netmask information
    static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet);

910 911 912
    uint16_t port_;  ///< UDP port number on which server listens.
    bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).

913 914
protected:

915 916
    /// @brief Holds information about disabled DHCP service and/or
    /// disabled subnet/network scopes.
917
    NetworkStatePtr network_state_;
918

919
public:
Francis Dupont's avatar
Francis Dupont committed
920
    /// Class methods for DHCPv4-over-DHCPv6 handler
921

922 923 924 925 926
    /// @brief Updates statistics for received packets
    /// @param query packet received
    static void processStatsReceived(const Pkt4Ptr& query);

    /// @brief Updates statistics for transmitted packets
Francis Dupont's avatar
Francis Dupont committed
927
    /// @param response packet transmitted
928
    static void processStatsSent(const Pkt4Ptr& response);
929

Francis Dupont's avatar
Francis Dupont committed
930 931 932
    /// @brief Returns the index for "buffer4_receive" hook point
    /// @return the index for "buffer4_receive" hook point
    static int getHookIndexBuffer4Receive();
933

Francis Dupont's avatar
Francis Dupont committed
934 935 936
    /// @brief Returns the index for "pkt4_receive" hook point
    /// @return the index for "pkt4_receive" hook point
    static int getHookIndexPkt4Receive();
937

Francis Dupont's avatar
Francis Dupont committed
938 939 940
    /// @brief Returns the index for "subnet4_select" hook point
    /// @return the index for "subnet4_select" hook point
    static int getHookIndexSubnet4Select();
941

Francis Dupont's avatar
Francis Dupont committed
942 943 944
    /// @brief Returns the index for "lease4_release" hook point
    /// @return the index for "lease4_release" hook point
    static int getHookIndexLease4Release();
945

Francis Dupont's avatar
Francis Dupont committed
946 947 948
    /// @brief Returns the index for "pkt4_send" hook point
    /// @return the index for "pkt4_send" hook point
    static int getHookIndexPkt4Send();
949

Francis Dupont's avatar
Francis Dupont committed
950 951 952
    /// @brief Returns the index for "buffer4_send" hook point
    /// @return the index for "buffer4_send" hook point
    static int getHookIndexBuffer4Send();
953

Francis Dupont's avatar
Francis Dupont committed
954 955 956
    /// @brief Returns the index for "lease4_decline" hook point
    /// @return the index for "lease4_decline" hook point
    static int getHookIndexLease4Decline();
957 958 959 960 961 962
};

}; // namespace isc::dhcp
}; // namespace isc

#endif // DHCP4_SRV_H