Parcourir la source

[4097a] Ported DHCPv4 code to DHCPv6 (unfinished)

Francis Dupont il y a 9 ans
Parent
commit
e0c80090d6

+ 4 - 1
src/bin/dhcp4/dhcp4_messages.mes

@@ -85,7 +85,10 @@ information. The second argument includes all classes to which the
 packet has been assigned.
 packet has been assigned.
 
 
 % DHCP4_CLASS_UNCONFIGURED %1: client packet belongs an unconfigured class: %2
 % 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
 % 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
 This debug message is issued when the server is processing the DHCPv4 message

+ 1 - 1
src/bin/dhcp4/dhcp4_srv.cc

@@ -887,8 +887,8 @@ Dhcpv4Srv::appendRequestedOptions(Dhcpv4Exchange& ex) {
          opt != requested_opts.end(); ++opt) {
          opt != requested_opts.end(); ++opt) {
         // Add nothing when it is already there
         // Add nothing when it is already there
         if (!resp->getOption(*opt)) {
         if (!resp->getOption(*opt)) {
-            const CfgOptionList& co_list = ex.getCfgOptionList();
             // Iterate on the configured option list
             // Iterate on the configured option list
+            const CfgOptionList& co_list = ex.getCfgOptionList();
             for (CfgOptionList::const_iterator copts = co_list.begin();
             for (CfgOptionList::const_iterator copts = co_list.begin();
                  copts != co_list.end(); ++copts) {
                  copts != co_list.end(); ++copts) {
                 OptionDescriptor desc = (*copts)->get("dhcp4", *opt);
                 OptionDescriptor desc = (*copts)->get("dhcp4", *opt);

+ 1 - 0
src/bin/dhcp6/Makefile.am

@@ -75,6 +75,7 @@ kea_dhcp6_SOURCES  = main.cc
 
 
 kea_dhcp6_LDADD  = libdhcp6.la
 kea_dhcp6_LDADD  = libdhcp6.la
 kea_dhcp6_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.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/dhcpsrv/libkea-dhcpsrv.la
 kea_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.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
 kea_dhcp6_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la

+ 6 - 0
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
 This debug message informs that incoming packet has been assigned to specified
 class or classes.
 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
 % DHCP6_COMMAND_RECEIVED received command %1, arguments: %2
 A debug message listing the command (and possible arguments) received
 A debug message listing the command (and possible arguments) received
 from the Kea control system by the IPv6 DHCP server.
 from the Kea control system by the IPv6 DHCP server.

+ 112 - 34
src/bin/dhcp6/dhcp6_srv.cc

@@ -42,6 +42,8 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_selector.h>
 #include <dhcpsrv/subnet_selector.h>
 #include <dhcpsrv/utils.h>
 #include <dhcpsrv/utils.h>
+#include <eval/evaluate.h>
+#include <eval/eval_messages.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <hooks/callout_handle.h>
 #include <hooks/callout_handle.h>
 #include <hooks/hooks_log.h>
 #include <hooks/hooks_log.h>
@@ -888,6 +890,36 @@ Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr&, Pkt6Ptr& answer) {
 }
 }
 
 
 void
 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,
 Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
                                   AllocEngine::ClientContext6& ctx) {
                                   AllocEngine::ClientContext6& ctx) {
 
 
@@ -902,32 +934,18 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
         return;
         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.
     // Get the list of options that client requested.
     const std::vector<uint16_t>& requested_opts = option_oro->getValues();
     const std::vector<uint16_t>& requested_opts = option_oro->getValues();
     BOOST_FOREACH(uint16_t opt, requested_opts) {
     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_) {
             if (desc.option_) {
                 answer->addOption(desc.option_);
                 answer->addOption(desc.option_);
+                break;
             }
             }
         }
         }
     }
     }
@@ -972,10 +990,15 @@ Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer
     bool added = false;
     bool added = false;
     const std::vector<uint16_t>& requested_opts = oro->getValues();
     const std::vector<uint16_t>& requested_opts = oro->getValues();
     BOOST_FOREACH(uint16_t opt, requested_opts) {
     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);
     copyClientOptions(solicit, response);
+    buildCfgOptionList(solicit, ctx);
     appendDefaultOptions(solicit, response);
     appendDefaultOptions(solicit, response);
     appendRequestedOptions(solicit, response, ctx);
     appendRequestedOptions(solicit, response, ctx);
     appendRequestedVendorOptions(solicit, response, ctx);
     appendRequestedVendorOptions(solicit, response, ctx);
@@ -2324,6 +2348,7 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
 
 
     copyClientOptions(request, reply);
     copyClientOptions(request, reply);
+    buildCfgOptionList(request, ctx);
     appendDefaultOptions(request, reply);
     appendDefaultOptions(request, reply);
     appendRequestedOptions(request, reply, ctx);
     appendRequestedOptions(request, reply, ctx);
     appendRequestedVendorOptions(request, reply, ctx);
     appendRequestedVendorOptions(request, reply, ctx);
@@ -2347,6 +2372,7 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
 
 
     copyClientOptions(renew, reply);
     copyClientOptions(renew, reply);
+    buildCfgOptionList(renew, ctx);
     appendDefaultOptions(renew, reply);
     appendDefaultOptions(renew, reply);
     appendRequestedOptions(renew, reply, ctx);
     appendRequestedOptions(renew, reply, ctx);
 
 
@@ -2369,6 +2395,7 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
 
 
     copyClientOptions(rebind, reply);
     copyClientOptions(rebind, reply);
+    buildCfgOptionList(rebind, ctx);
     appendDefaultOptions(rebind, reply);
     appendDefaultOptions(rebind, reply);
     appendRequestedOptions(rebind, reply, ctx);
     appendRequestedOptions(rebind, reply, ctx);
 
 
@@ -2399,7 +2426,9 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
     // Make sure that the necessary options are included.
     // Make sure that the necessary options are included.
     copyClientOptions(confirm, reply);
     copyClientOptions(confirm, reply);
+    buildCfgOptionList(confirm, ctx);
     appendDefaultOptions(confirm, reply);
     appendDefaultOptions(confirm, reply);
+    appendRequestedOptions(confirm, reply, ctx);
     // Indicates if at least one address has been verified. If no addresses
     // 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
     // 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.
     // 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)
     // Copy client options (client-id, also relay information if present)
     copyClientOptions(inf_request, reply);
     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
     // 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
     // to put in all messages it sends (server-id for now, but possibly other
     // options once we start supporting authentication)
     // options once we start supporting authentication)
@@ -2891,7 +2923,7 @@ Dhcpv6Srv::unpackOptions(const OptionBuffer& buf,
     return (offset);
     return (offset);
 }
 }
 
 
-void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
+void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt, std::string& classes) {
     OptionVendorClassPtr vclass = boost::dynamic_pointer_cast<
     OptionVendorClassPtr vclass = boost::dynamic_pointer_cast<
         OptionVendorClass>(pkt->getOption(D6O_VENDOR_CLASS));
         OptionVendorClass>(pkt->getOption(D6O_VENDOR_CLASS));
 
 
@@ -2899,22 +2931,68 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
         return;
         return;
     }
     }
 
 
-    std::ostringstream classes;
     if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) {
     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)) {
     } 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 {
     } 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)
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED)
-            .arg(classes.str());
+            .arg(classes);
     }
     }
 }
 }
 
 

+ 36 - 3
src/bin/dhcp6/dhcp6_srv.h

@@ -24,6 +24,7 @@
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcpsrv/alloc_engine.h>
 #include <dhcpsrv/alloc_engine.h>
+#include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/d2_client_mgr.h>
 #include <dhcpsrv/d2_client_mgr.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <hooks/callout_handle.h>
 #include <hooks/callout_handle.h>
@@ -428,6 +429,25 @@ protected:
     /// @param answer server's message (options will be copied here)
     /// @param answer server's message (options will be copied here)
     void copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
     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.
     /// @brief Appends default options to server's answer.
     ///
     ///
     /// Adds required options to server's answer. In particular, server-id
     /// 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.
     /// @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).
     /// @ref isc::dhcp::Pkt6::inClass).
     ///
     ///
     /// @param pkt packet to be classified
     /// @param pkt packet to be classified
@@ -739,6 +761,14 @@ protected:
 
 
 private:
 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.
     /// @brief Generate FQDN to be sent to a client if none exists.
     ///
     ///
     /// This function is meant to be called by the functions which process
     /// This function is meant to be called by the functions which process
@@ -795,6 +825,9 @@ private:
     /// UDP port number on which server listens.
     /// UDP port number on which server listens.
     uint16_t port_;
     uint16_t port_;
 
 
+    /// @brief Configured option list for appending otions.
+    CfgOptionList cfg_option_list_;
+
 protected:
 protected:
 
 
     /// Indicates if shutdown is in progress. Setting it to true will
     /// Indicates if shutdown is in progress. Setting it to true will

+ 1 - 0
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/cfgrpt/libcfgrpt.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.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/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/dhcp_ddns/libkea-dhcp_ddns.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la