Commit e0c80090 authored by Francis Dupont's avatar Francis Dupont
Browse files

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

parent b925400a
......@@ -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
......
......@@ -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);
......
......@@ -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
......
......@@ -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.
......
......@@ -42,6 +42,8 @@
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/subnet_selector.h>
#include <dhcpsrv/utils.h>
#include <eval/evaluate.h>
#include <eval/eval_messages.h>
#include <exceptions/exceptions.h>
#include <hooks/callout_handle.h>
#include <hooks/hooks_log.h>
......@@ -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<uint16_t>& 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<uint16_t>& 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);
}
}
......
......@@ -24,6 +24,7 @@
#include <dhcp/option_definition.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/subnet.h>
#include <hooks/callout_handle.h>
......@@ -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
......
......@@ -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
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment