From: Francis Dupont Date: Sat, 21 Nov 2015 14:40:33 +0000 (+0100) Subject: [4097a] Ported DHCPv4 code to DHCPv6 (unfinished) X-Git-Tag: trac4204fd_base~2^2~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e0c80090d6045d6930b5d60a39af50e7950ac33e;p=thirdparty%2Fkea.git [4097a] Ported DHCPv4 code to DHCPv6 (unfinished) --- diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index ff9d5a1e51..0a75ccb92c 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -85,7 +85,10 @@ information. The second argument includes all classes to which the packet has been assigned. % DHCP4_CLASS_UNCONFIGURED %1: client packet belongs an unconfigured class: %2 -This debug message informs that incoming packet belongs to a class which cannot be found in the configuration. Either a hook written before the classification was added to Kea is used, or class naming is inconsistent. +This debug message informs that incoming packet belongs to a class +which cannot be found in the configuration. Either a hook written +before the classification was added to Kea is used, or class naming is +inconsistent. % DHCP4_CLIENTID_IGNORED_FOR_LEASES %1: not using client identifier for lease allocation for subnet %2 This debug message is issued when the server is processing the DHCPv4 message diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 428dd9ffc3..ba520b55d4 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -887,8 +887,8 @@ Dhcpv4Srv::appendRequestedOptions(Dhcpv4Exchange& ex) { opt != requested_opts.end(); ++opt) { // Add nothing when it is already there if (!resp->getOption(*opt)) { - const CfgOptionList& co_list = ex.getCfgOptionList(); // Iterate on the configured option list + const CfgOptionList& co_list = ex.getCfgOptionList(); for (CfgOptionList::const_iterator copts = co_list.begin(); copts != co_list.end(); ++copts) { OptionDescriptor desc = (*copts)->get("dhcp4", *opt); diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 324aea80f2..6fb8f08809 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -75,6 +75,7 @@ kea_dhcp6_SOURCES = main.cc kea_dhcp6_LDADD = libdhcp6.la kea_dhcp6_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +kea_dhcp6_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la kea_dhcp6_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la kea_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la kea_dhcp6_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index cda6ad0828..20e6c8e40b 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -88,6 +88,12 @@ to establish a session with the Kea control channel. This debug message informs that incoming packet has been assigned to specified class or classes. +% DHCP6_CLASS_UNCONFIGURED client packet belongs an unconfigured class: %1 +This debug message informs that incoming packet belongs to a class +which cannot be found in the configuration. Either a hook written +before the classification was added to Kea is used, or class naming is +inconsistent. + % DHCP6_COMMAND_RECEIVED received command %1, arguments: %2 A debug message listing the command (and possible arguments) received from the Kea control system by the IPv6 DHCP server. diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 2f0687b60f..6d2bb42920 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include #include @@ -887,6 +889,36 @@ Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr&, Pkt6Ptr& answer) { answer->addOption(getServerID()); } +void +Dhcpv6Srv::buildCfgOptionList(const Pkt6Ptr& question, + AllocEngine::ClientContext6& ctx) { + CfgOptionList& co_list = getCfgOptionList(); + + // First subnet configured options + if (ctx.subnet_) { + co_list.push_back(ctx.subnet_->getCfgOption()); + } + + // Each class in the incoming packet + const ClientClasses& classes = question->getClasses(); + for (ClientClasses::const_iterator cclass = classes.begin(); + cclass != classes.end(); ++cclass) { + // Find the client class definition for this class + const ClientClassDefPtr& ccdef = CfgMgr::instance().getCurrentCfg()-> + getClientClassDictionary()->findClass(*cclass); + if (!ccdef) { + // Not found: the class is not configured + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_UNCONFIGURED) + .arg(*cclass); + continue; + } + co_list.push_back(ccdef->getCfgOption()); + } + + // Last global options + co_list.push_back(CfgMgr::instance().getCurrentCfg()->getCfgOption()); +} + void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer, AllocEngine::ClientContext6& ctx) { @@ -902,32 +934,18 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer, return; } - // Get global option definitions (i.e. options defined to apply to all, - // unless overwritten on a subnet or host level) - ConstCfgOptionPtr global_opts = CfgMgr::instance().getCurrentCfg()-> - getCfgOption(); - // Get the list of options that client requested. const std::vector& requested_opts = option_oro->getValues(); BOOST_FOREACH(uint16_t opt, requested_opts) { - // If we found a subnet for this client, all options (including the - // global options) should be available through the options - // configuration for the subnet. - if (ctx.subnet_) { - OptionDescriptor desc = ctx.subnet_->getCfgOption()->get("dhcp6", - opt); - if (desc.option_) { - // Attempt to assign an option from subnet first. - answer->addOption(desc.option_); - continue; - } - - // If there is no subnet selected (e.g. Information-request message - // case) we need to look at the global options. - } else { - OptionDescriptor desc = global_opts->get("dhcp6", opt); + // Iterate on the configured option list + const CfgOptionList& co_list = getCfgOptionList(); + for (CfgOptionList::const_iterator copts = co_list.begin(); + copts != co_list.end(); ++copts) { + OptionDescriptor desc = (*copts)->get("dhcp6", opt); + // Got it: add it and jump to the outer loop if (desc.option_) { answer->addOption(desc.option_); + break; } } } @@ -972,10 +990,15 @@ Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer bool added = false; const std::vector& requested_opts = oro->getValues(); BOOST_FOREACH(uint16_t opt, requested_opts) { - OptionDescriptor desc = ctx.subnet_->getCfgOption()->get(vendor_id, opt); - if (desc.option_) { - vendor_rsp->addOption(desc.option_); - added = true; + const CfgOptionList& co_list = getCfgOptionList(); + for (CfgOptionList::const_iterator copts = co_list.begin(); + copts != co_list.end(); ++copts) { + OptionDescriptor desc = (*copts)->get(vendor_id, opt); + if (desc.option_) { + vendor_rsp->addOption(desc.option_); + added = true; + break; + } } } @@ -2297,6 +2320,7 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) { } copyClientOptions(solicit, response); + buildCfgOptionList(solicit, ctx); appendDefaultOptions(solicit, response); appendRequestedOptions(solicit, response, ctx); appendRequestedVendorOptions(solicit, response, ctx); @@ -2324,6 +2348,7 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) { Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid())); copyClientOptions(request, reply); + buildCfgOptionList(request, ctx); appendDefaultOptions(request, reply); appendRequestedOptions(request, reply, ctx); appendRequestedVendorOptions(request, reply, ctx); @@ -2347,6 +2372,7 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) { Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid())); copyClientOptions(renew, reply); + buildCfgOptionList(renew, ctx); appendDefaultOptions(renew, reply); appendRequestedOptions(renew, reply, ctx); @@ -2369,6 +2395,7 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) { Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid())); copyClientOptions(rebind, reply); + buildCfgOptionList(rebind, ctx); appendDefaultOptions(rebind, reply); appendRequestedOptions(rebind, reply, ctx); @@ -2399,7 +2426,9 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) { Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid())); // Make sure that the necessary options are included. copyClientOptions(confirm, reply); + buildCfgOptionList(confirm, ctx); appendDefaultOptions(confirm, reply); + appendRequestedOptions(confirm, reply, ctx); // Indicates if at least one address has been verified. If no addresses // are verified it means that the client has sent no IA_NA options // or no IAAddr options and that client's message has to be discarded. @@ -2778,6 +2807,9 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& inf_request) { // Copy client options (client-id, also relay information if present) copyClientOptions(inf_request, reply); + // Build the configured option list for append methods + buildCfgOptionList(inf_request, ctx); + // Append default options, i.e. options that the server is supposed // to put in all messages it sends (server-id for now, but possibly other // options once we start supporting authentication) @@ -2891,7 +2923,7 @@ Dhcpv6Srv::unpackOptions(const OptionBuffer& buf, return (offset); } -void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) { +void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt, std::string& classes) { OptionVendorClassPtr vclass = boost::dynamic_pointer_cast< OptionVendorClass>(pkt->getOption(D6O_VENDOR_CLASS)); @@ -2899,22 +2931,68 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) { return; } - std::ostringstream classes; if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) { - classes << VENDOR_CLASS_PREFIX << DOCSIS3_CLASS_MODEM; + pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM); + classes += VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM + " "; } else if (vclass->hasTuple(DOCSIS3_CLASS_EROUTER)) { - classes << VENDOR_CLASS_PREFIX << DOCSIS3_CLASS_EROUTER; + pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER); + classes += VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER + " "; } else { - classes << VENDOR_CLASS_PREFIX << vclass->getTuple(0).getText(); + pkt->addClass(VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText()); + classes + VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText() + " "; + } +} + +void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) { + string classes = ""; + + // First phase: built-in vendor class processing + classifyByVendor(pkt, classes); + + // Run match expressions + // Note getClientClassDictionary() cannot be null + const ClientClassDefMapPtr& defs_ptr = CfgMgr::instance().getCurrentCfg()-> + getClientClassDictionary()->getClasses(); + for (ClientClassDefMap::const_iterator it = defs_ptr->begin(); + it != defs_ptr->end(); ++it) { + // Note second cannot be null + const ExpressionPtr& expr_ptr = it->second->getMatchExpr(); + // Nothing to do without an expression to evaluate + if (!expr_ptr) { + continue; + } + // Evaluate the expression which can return false (no match), + // true (match) or raise an exception (error) + try { + bool status = evaluate(*expr_ptr, *pkt); + if (status) { + LOG_INFO(dhcp6_logger, EVAL_RESULT) + .arg(it->first) + .arg(status); + // Matching: add the class + pkt->addClass(it->first); + classes += it->first + " "; + } else { + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, EVAL_RESULT) + .arg(it->first) + .arg(status); + } + } catch (const Exception& ex) { + LOG_ERROR(dhcp6_logger, EVAL_RESULT) + .arg(it->first) + .arg(ex.what()); + } catch (...) { + LOG_ERROR(dhcp6_logger, EVAL_RESULT) + .arg(it->first) + .arg("get exception?"); + } } - // If there is no class identified, leave. - if (!classes.str().empty()) { - pkt->addClass(classes.str()); + if (!classes.empty()) { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED) - .arg(classes.str()); + .arg(classes); } } diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 1609af068c..d8bb024b06 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -428,6 +429,25 @@ protected: /// @param answer server's message (options will be copied here) void copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer); + /// @brief Returns the configured option list + CfgOptionList& getCfgOptionList() { + return (cfg_option_list_); + } + + /// @brief Returns the configured option list + const CfgOptionList& getCfgOptionList() const { + return (cfg_option_list_); + } + + /// @brief Build the configured option list + /// + /// @note The configured option list is an *ordered* list of + /// @c CfgOption objects used to append options to the response. + /// + /// @param ex The exchange where the configured option list is cached + void buildCfgOptionList(const Pkt6Ptr& question, + AllocEngine::ClientContext6& ctx); + /// @brief Appends default options to server's answer. /// /// Adds required options to server's answer. In particular, server-id @@ -633,9 +653,11 @@ protected: /// @brief Assigns incoming packet to zero or more classes. /// - /// @note For now, the client classification is very simple. It just uses - /// content of the vendor-class-identifier option as a class. The resulting - /// class will be stored in packet (see @ref isc::dhcp::Pkt6::classes_ and + /// @note This is done in two phases: first the content of the + /// vendor-class-identifier option is used as a class, by + /// calling @ref classifyByVendor(). Second classification match + /// expressions are evaluated. The resulting classes will be stored + /// in the packet (see @ref isc::dhcp::Pkt6::classes_ and /// @ref isc::dhcp::Pkt6::inClass). /// /// @param pkt packet to be classified @@ -739,6 +761,14 @@ protected: private: + /// @brief Assign class using vendor-class-identifier option + /// + /// @note This is the first part of @ref classifyPacket + /// + /// @param pkt packet to be classified + /// @param classes a reference to added class names for logging + void classifyByVendor(const Pkt6Ptr& pkt, std::string& classes); + /// @brief Generate FQDN to be sent to a client if none exists. /// /// This function is meant to be called by the functions which process @@ -795,6 +825,9 @@ private: /// UDP port number on which server listens. uint16_t port_; + /// @brief Configured option list for appending otions. + CfgOptionList cfg_option_list_; + protected: /// Indicates if shutdown is in progress. Setting it to true will diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 10fe4c35a8..58a518dbf3 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -112,6 +112,7 @@ dhcp6_unittests_LDADD = $(top_builddir)/src/bin/dhcp6/libdhcp6.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/testutils/libdhcpsrvtest.la +dhcp6_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la