mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-05 00:15:17 +00:00
[4097a] Rebased and updated trac4097 code (still some comments to address)
This commit is contained in:
@@ -75,6 +75,7 @@ kea_dhcp4_SOURCES = main.cc
|
|||||||
kea_dhcp4_LDADD = libdhcp4.la
|
kea_dhcp4_LDADD = libdhcp4.la
|
||||||
kea_dhcp4_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la
|
kea_dhcp4_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la
|
||||||
kea_dhcp4_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
|
kea_dhcp4_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
|
||||||
|
kea_dhcp4_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
|
||||||
kea_dhcp4_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
|
kea_dhcp4_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
|
||||||
kea_dhcp4_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
|
kea_dhcp4_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
|
||||||
kea_dhcp4_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
|
kea_dhcp4_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
|
||||||
|
@@ -38,6 +38,8 @@
|
|||||||
#include <dhcpsrv/subnet_selector.h>
|
#include <dhcpsrv/subnet_selector.h>
|
||||||
#include <dhcpsrv/utils.h>
|
#include <dhcpsrv/utils.h>
|
||||||
#include <dhcpsrv/utils.h>
|
#include <dhcpsrv/utils.h>
|
||||||
|
#include <eval/evaluate.h>
|
||||||
|
#include <eval/eval_messages.h>
|
||||||
#include <hooks/callout_handle.h>
|
#include <hooks/callout_handle.h>
|
||||||
#include <hooks/hooks_log.h>
|
#include <hooks/hooks_log.h>
|
||||||
#include <hooks/hooks_manager.h>
|
#include <hooks/hooks_manager.h>
|
||||||
@@ -2242,13 +2244,14 @@ Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
|
void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
|
||||||
|
string classes = "";
|
||||||
|
|
||||||
|
// Built-in vendor class processing
|
||||||
boost::shared_ptr<OptionString> vendor_class =
|
boost::shared_ptr<OptionString> vendor_class =
|
||||||
boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER));
|
boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER));
|
||||||
|
|
||||||
string classes = "";
|
|
||||||
|
|
||||||
if (!vendor_class) {
|
if (!vendor_class) {
|
||||||
return;
|
goto vendor_class_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOCSIS specific section
|
// DOCSIS specific section
|
||||||
@@ -2275,8 +2278,49 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
|
|||||||
pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER);
|
pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER);
|
||||||
classes += string(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER) + " ";
|
classes += string(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER) + " ";
|
||||||
} else {
|
} else {
|
||||||
classes += VENDOR_CLASS_PREFIX + vendor_class->getValue();
|
|
||||||
pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
|
pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
|
||||||
|
classes += VENDOR_CLASS_PREFIX + vendor_class->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
vendor_class_done:
|
||||||
|
|
||||||
|
// 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(options4_logger, EVAL_RESULT)
|
||||||
|
.arg(it->first)
|
||||||
|
.arg(status);
|
||||||
|
// Matching: add the class
|
||||||
|
pkt->addClass(it->first);
|
||||||
|
classes += it->first + " ";
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, EVAL_RESULT)
|
||||||
|
.arg(it->first)
|
||||||
|
.arg(status);
|
||||||
|
}
|
||||||
|
} catch (const Exception& ex) {
|
||||||
|
LOG_ERROR(options4_logger, EVAL_RESULT)
|
||||||
|
.arg(it->first)
|
||||||
|
.arg(ex.what());
|
||||||
|
} catch (...) {
|
||||||
|
LOG_ERROR(options4_logger, EVAL_RESULT)
|
||||||
|
.arg(it->first)
|
||||||
|
.arg("get exception?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!classes.empty()) {
|
if (!classes.empty()) {
|
||||||
@@ -2298,6 +2342,7 @@ Dhcpv4Srv::classSpecificProcessing(const Dhcpv4Exchange& ex) {
|
|||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DOCSIS3 class modem specific processing
|
||||||
if (query->inClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM)) {
|
if (query->inClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM)) {
|
||||||
|
|
||||||
// Set next-server. This is TFTP server address. Cable modems will
|
// Set next-server. This is TFTP server address. Cable modems will
|
||||||
@@ -2319,12 +2364,39 @@ Dhcpv4Srv::classSpecificProcessing(const Dhcpv4Exchange& ex) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DOCSIS3 class erouter specific processing
|
||||||
if (query->inClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER)) {
|
if (query->inClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER)) {
|
||||||
|
|
||||||
// Do not set TFTP server address for eRouter devices.
|
// Do not set TFTP server address for eRouter devices.
|
||||||
rsp->setSiaddr(IOAddress::IPV4_ZERO_ADDRESS());
|
rsp->setSiaddr(IOAddress::IPV4_ZERO_ADDRESS());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process each class
|
||||||
|
const ClientClassDefMapPtr& defs_ptr = CfgMgr::instance().getCurrentCfg()->
|
||||||
|
getClientClassDictionary()->getClasses();
|
||||||
|
for (ClientClassDefMap::const_iterator it = defs_ptr->begin();
|
||||||
|
it != defs_ptr->end(); ++it) {
|
||||||
|
// Is the query in this class?
|
||||||
|
if (!it->second || !query->inClass(it->first)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Get the configured options of this class
|
||||||
|
const OptionContainerPtr& options =
|
||||||
|
it->second->getCfgOption()->getAll("dhcp4");
|
||||||
|
if (!options || options->empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Go through each OptionDescriptor
|
||||||
|
for (OptionContainer::const_iterator desc = options->begin();
|
||||||
|
desc != options->end(); ++desc) {
|
||||||
|
OptionPtr opt = desc->option_;
|
||||||
|
// Add the option if it doesn't exist yet
|
||||||
|
if (!rsp->getOption(opt->getType())) {
|
||||||
|
rsp->addOption(opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -706,10 +706,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
|
/// @note It is done in two phases: first the content of the
|
||||||
/// content of the vendor-class-identifier option as a class. The resulting
|
/// vendor-class-identifier option is used as a class. Second
|
||||||
/// class will be stored in packet (see @ref isc::dhcp::Pkt4::classes_ and
|
/// classification match expressions are evaluated. The resulting
|
||||||
/// @ref isc::dhcp::Pkt4::inClass).
|
/// class will be stored in packet (see @ref isc::dhcp::Pkt4::classes_
|
||||||
|
/// and @ref isc::dhcp::Pkt4::inClass).
|
||||||
///
|
///
|
||||||
/// @param pkt packet to be classified
|
/// @param pkt packet to be classified
|
||||||
void classifyPacket(const Pkt4Ptr& pkt);
|
void classifyPacket(const Pkt4Ptr& pkt);
|
||||||
|
@@ -109,6 +109,7 @@ dhcp4_unittests_LDADD = $(top_builddir)/src/bin/dhcp4/libdhcp4.la
|
|||||||
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la
|
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la
|
||||||
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
|
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
|
||||||
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/testutils/libdhcpsrvtest.la
|
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/testutils/libdhcpsrvtest.la
|
||||||
|
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
|
||||||
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
|
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
|
||||||
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
|
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
|
||||||
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
|
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
|
||||||
|
@@ -1646,8 +1646,8 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsDocsisDefinitions) {
|
|||||||
ASSERT_EQ(0, rcode_);
|
ASSERT_EQ(0, rcode_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if client packets are classified properly
|
// Checks if DOCSIS client packets are classified properly
|
||||||
TEST_F(Dhcpv4SrvTest, clientClassification) {
|
TEST_F(Dhcpv4SrvTest, docsisClientClassification) {
|
||||||
|
|
||||||
NakedDhcpv4Srv srv(0);
|
NakedDhcpv4Srv srv(0);
|
||||||
|
|
||||||
@@ -1674,10 +1674,77 @@ TEST_F(Dhcpv4SrvTest, clientClassification) {
|
|||||||
EXPECT_FALSE(dis2->inClass(srv.VENDOR_CLASS_PREFIX + "docsis3.0"));
|
EXPECT_FALSE(dis2->inClass(srv.VENDOR_CLASS_PREFIX + "docsis3.0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if client packets are classified properly using match expressions.
|
||||||
|
TEST_F(Dhcpv4SrvTest, matchClassification) {
|
||||||
|
NakedDhcpv4Srv srv(0);
|
||||||
|
|
||||||
|
// The router class matches incoming packets with foo in a host-name
|
||||||
|
// option (code 12) and sets an ip-forwarding option in the response
|
||||||
|
string config = "{ \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ \"*\" ] }, "
|
||||||
|
"\"rebind-timer\": 2000, "
|
||||||
|
"\"renew-timer\": 1000, "
|
||||||
|
"\"valid-lifetime\": 4000, "
|
||||||
|
"\"subnet4\": [ "
|
||||||
|
"{ \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
|
||||||
|
" \"subnet\": \"192.0.2.0/24\" } ], "
|
||||||
|
"\"client-classes\": [ "
|
||||||
|
"{ \"name\": \"router\","
|
||||||
|
" \"option-data\": ["
|
||||||
|
" { \"name\": \"ip-forwarding\","
|
||||||
|
" \"data\": \"true\" } ],"
|
||||||
|
" \"test\": \"option[12] == 'foo'\" } ] }";
|
||||||
|
|
||||||
|
ElementPtr json = Element::fromJSON(config);
|
||||||
|
ConstElementPtr status;
|
||||||
|
|
||||||
|
// Configure the server and make sure the config is accepted
|
||||||
|
EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
|
||||||
|
ASSERT_TRUE(status);
|
||||||
|
comment_ = config::parseAnswer(rcode_, status);
|
||||||
|
ASSERT_EQ(0, rcode_);
|
||||||
|
|
||||||
|
CfgMgr::instance().commit();
|
||||||
|
|
||||||
|
// Create packets with enough to select the subnet
|
||||||
|
Pkt4Ptr query1(new Pkt4(DHCPDISCOVER, 1234));
|
||||||
|
query1->setRemoteAddr(IOAddress("192.0.2.1"));
|
||||||
|
Pkt4Ptr query2(new Pkt4(DHCPDISCOVER, 1234));
|
||||||
|
query2->setRemoteAddr(IOAddress("192.0.2.1"));
|
||||||
|
|
||||||
|
// Create and add a host-name option to the first query
|
||||||
|
OptionStringPtr hostname(new OptionString(Option::V4, 12, "foo"));
|
||||||
|
ASSERT_TRUE(hostname);
|
||||||
|
query1->addOption(hostname);
|
||||||
|
|
||||||
|
// Classify packets
|
||||||
|
srv.classifyPacket(query1);
|
||||||
|
srv.classifyPacket(query2);
|
||||||
|
|
||||||
|
// The first packet (and only the first) should be in the router class
|
||||||
|
EXPECT_TRUE(query1->inClass("router"));
|
||||||
|
EXPECT_FALSE(query2->inClass("router"));
|
||||||
|
|
||||||
|
Dhcpv4Exchange ex1 = createExchange(query1);
|
||||||
|
Pkt4Ptr response1 = ex1.getResponse();
|
||||||
|
Dhcpv4Exchange ex2 = createExchange(query2);
|
||||||
|
Pkt4Ptr response2 = ex2.getResponse();
|
||||||
|
|
||||||
|
// Classification processing should add an ip-forwarding option
|
||||||
|
srv.classSpecificProcessing(ex1);
|
||||||
|
OptionPtr opt1 = response1->getOption(DHO_IP_FORWARDING);
|
||||||
|
EXPECT_TRUE(opt1);
|
||||||
|
|
||||||
|
// But only for the first exchange
|
||||||
|
srv.classSpecificProcessing(ex2);
|
||||||
|
OptionPtr opt2 = response2->getOption(DHO_IP_FORWARDING);
|
||||||
|
EXPECT_FALSE(opt2);
|
||||||
|
}
|
||||||
|
|
||||||
// Checks if the client-class field is indeed used for subnet selection.
|
// Checks if the client-class field is indeed used for subnet selection.
|
||||||
// Note that packet classification is already checked in Dhcpv4SrvTest
|
// Note that packet classification is already checked in Dhcpv4SrvTest
|
||||||
// .clientClassification above.
|
// .*clientClassification above.
|
||||||
TEST_F(Dhcpv4SrvTest, clientClassify2) {
|
TEST_F(Dhcpv4SrvTest, clientClassify) {
|
||||||
|
|
||||||
// This test configures 2 subnets. We actually only need the
|
// This test configures 2 subnets. We actually only need the
|
||||||
// first one, but since there's still this ugly hack that picks
|
// first one, but since there's still this ugly hack that picks
|
||||||
|
@@ -204,6 +204,7 @@ public:
|
|||||||
using Dhcpv4Srv::srvidToString;
|
using Dhcpv4Srv::srvidToString;
|
||||||
using Dhcpv4Srv::unpackOptions;
|
using Dhcpv4Srv::unpackOptions;
|
||||||
using Dhcpv4Srv::classifyPacket;
|
using Dhcpv4Srv::classifyPacket;
|
||||||
|
using Dhcpv4Srv::classSpecificProcessing;
|
||||||
using Dhcpv4Srv::accept;
|
using Dhcpv4Srv::accept;
|
||||||
using Dhcpv4Srv::acceptMessageType;
|
using Dhcpv4Srv::acceptMessageType;
|
||||||
using Dhcpv4Srv::selectSubnet;
|
using Dhcpv4Srv::selectSubnet;
|
||||||
|
Reference in New Issue
Block a user