"Fossies" - the Fresh Open Source Software Archive

Member "kea-1.6.2/src/bin/dhcp4/dhcp4_srv.cc" (21 Feb 2020, 146427 Bytes) of package /linux/misc/kea-1.6.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "dhcp4_srv.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.6.1_vs_1.6.2.

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