Commit 217be453 authored by Francis Dupont's avatar Francis Dupont

[606-drop-packets-in-drop-class] Added DROP class (including doc and unit tests)

parent c8230cca
......@@ -90,6 +90,11 @@
resort) definition.
</para></listitem>
<listitem><para>
When the incoming packet belongs the special DROP class it is
dropped and an informational message is logged with the packet
information.
</para></listitem>
<listitem><para>
A subnet is chosen, possibly based on the class information when
some subnets are reserved. More precisely: when choosing a subnet,
the server iterates over all of the subnets that are
......@@ -111,10 +116,11 @@
request") evaluation - are processed in the order they are defined
in the configuration; the boolean expression is evaluated and,
if it returns true ("match"), the incoming packet is associated
with the class. After a subnet is selected, the server determines whether there is a reservation
for a given client. Therefore, it
with the class. After a subnet is selected, the server determines
whether there is a reservation for a given client. Therefore, it
is not possible to use KNOWN/UNKNOWN classes to select a shared
network or a subnet.
network or a subnet, nor to make the DROP class dependent of
KNOWN/UNKNOWN classes.
</para></listitem>
<listitem><para>
If needed, addresses and prefixes from pools are assigned,
......
......@@ -2534,7 +2534,8 @@ It is merely echoed by the server.
The first step is to assess an incoming packet and assign it to
zero or more classes.
The second step is to choose a subnet, possibly based on the
class information.
class information. When the incoming packet is in the "DROP"
special class it is dropped and an information message logged.
The next step is to evaluate class expressions depending on the
built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup,
using them for pool selection and assigning classes from host reservations.
......
......@@ -2429,7 +2429,8 @@ should include options from the new option space:
The first step is to assess an incoming packet and assign it to
zero or more classes.
Next, a subnet is chosen, possibly based on the
class information.
class information. When the incoming packet is inthe "DROP"
special class it is dropped and an information message logged.
After that, class expressions are evaluated depending on the
built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup,
using them for pool/pd-pool selection and assigning classes from host
......
......@@ -93,6 +93,7 @@ extern const isc::log::MessageID DHCP4_PACKET_DROP_0006 = "DHCP4_PACKET_DROP_000
extern const isc::log::MessageID DHCP4_PACKET_DROP_0007 = "DHCP4_PACKET_DROP_0007";
extern const isc::log::MessageID DHCP4_PACKET_DROP_0008 = "DHCP4_PACKET_DROP_0008";
extern const isc::log::MessageID DHCP4_PACKET_DROP_0009 = "DHCP4_PACKET_DROP_0009";
extern const isc::log::MessageID DHCP4_PACKET_DROP_DROP_CLASS = "DHCP4_PACKET_DROP_DROP_CLASS";
extern const isc::log::MessageID DHCP4_PACKET_NAK_0001 = "DHCP4_PACKET_NAK_0001";
extern const isc::log::MessageID DHCP4_PACKET_NAK_0002 = "DHCP4_PACKET_NAK_0002";
extern const isc::log::MessageID DHCP4_PACKET_NAK_0003 = "DHCP4_PACKET_NAK_0003";
......@@ -228,6 +229,7 @@ const char* values[] = {
"DHCP4_PACKET_DROP_0007", "%1: failed to process packet: %2",
"DHCP4_PACKET_DROP_0008", "%1: DHCP service is globally disabled",
"DHCP4_PACKET_DROP_0009", "%1: Option 53 missing (no DHCP message type), is this a BOOTP packet?",
"DHCP4_PACKET_DROP_DROP_CLASS", "dropped as member of the special DROP class: %1",
"DHCP4_PACKET_NAK_0001", "%1: failed to select a subnet for incoming packet, src %2, type %3",
"DHCP4_PACKET_NAK_0002", "%1: invalid address %2 requested by INIT-REBOOT",
"DHCP4_PACKET_NAK_0003", "%1: failed to advertise a lease, client sent ciaddr %2, requested-ip-address %3",
......
......@@ -94,6 +94,7 @@ extern const isc::log::MessageID DHCP4_PACKET_DROP_0006;
extern const isc::log::MessageID DHCP4_PACKET_DROP_0007;
extern const isc::log::MessageID DHCP4_PACKET_DROP_0008;
extern const isc::log::MessageID DHCP4_PACKET_DROP_0009;
extern const isc::log::MessageID DHCP4_PACKET_DROP_DROP_CLASS;
extern const isc::log::MessageID DHCP4_PACKET_NAK_0001;
extern const isc::log::MessageID DHCP4_PACKET_NAK_0002;
extern const isc::log::MessageID DHCP4_PACKET_NAK_0003;
......
......@@ -509,6 +509,10 @@ This debug message is issued when a packet is dropped because it did contain
option 53 and thus has no DHCP message type. The most likely explanation is
that it was BOOTP packet.
% DHCP4_PACKET_DROP_DROP_CLASS dropped as member of the special DROP class: %1
This informational message is emitted when an incoming packet was classified
into the special DROP class and dropped. The packet details are displayed.
% DHCP4_PACKET_NAK_0001 %1: failed to select a subnet for incoming packet, src %2, type %3
This error message is output when a packet was received from a subnet
for which the DHCPv4 server has not been configured. The most probable
......
......@@ -1006,6 +1006,14 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp, bool allow_packet_park) {
callout_handle->getArgument("query4", query);
}
// Check the DROP special class.
if (query->inClass("DROP")) {
LOG_INFO(packet4_logger, DHCP4_PACKET_DROP_DROP_CLASS)
.arg(query->toText());
// increase pkt4-receive-drop stats?
return;
}
AllocEngine::ClientContext4Ptr ctx;
try {
......
// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -75,6 +75,12 @@ namespace {
/// option[93].hex == 0x0009 aka telephones
/// option[93].hex == 0x0007 aka computers
///
/// - Configuration 5:
/// - Used for the DROP class
/// - 1 subnet: 10.0.0.0/24
/// - 1 pool: 10.0.0.10-10.0.0.100
/// - the following class defined: option[93].hex == 0x0009, DROP
///
const char* CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
......@@ -275,6 +281,22 @@ const char* CONFIGS[] = {
" } ]"
"}",
// Configuration 5
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"valid-lifetime\": 600,"
"\"client-classes\": ["
"{"
" \"name\": \"DROP\","
" \"test\": \"option[93].hex == 0x0009\""
"}],"
"\"subnet4\": [ { "
" \"subnet\": \"10.0.0.0/24\", "
" \"id\": 1,"
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]"
" } ]"
"}"
};
/// @brief Test fixture class for testing classification.
......@@ -1107,4 +1129,31 @@ TEST_F(ClassifyTest, precedenceNetwork) {
EXPECT_EQ("10.0.0.3", addrs[0].toText());
}
// This test checks the handling for the DROP special class.
TEST_F(ClassifyTest, dropClass) {
Dhcp4Client client(Dhcp4Client::SELECTING);
// Configure DHCP server.
configure(CONFIGS[5], *client.getServer());
// Send the discover.
client.doDiscover();
// No option: no drop.
EXPECT_TRUE(client.getContext().response_);
// Retry with an option matching the DROP class.
Dhcp4Client client2(Dhcp4Client::SELECTING);
// Add the pxe option.
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
client2.addExtraOption(pxe);
// Send the discover.
client2.doDiscover();
// Option, dropped.
EXPECT_FALSE(client2.getContext().response_);
}
} // end of anonymous namespace
......@@ -88,6 +88,7 @@ extern const isc::log::MessageID DHCP6_NO_SOCKETS_OPEN = "DHCP6_NO_SOCKETS_OPEN"
extern const isc::log::MessageID DHCP6_OPEN_SOCKET = "DHCP6_OPEN_SOCKET";
extern const isc::log::MessageID DHCP6_OPEN_SOCKET_FAIL = "DHCP6_OPEN_SOCKET_FAIL";
extern const isc::log::MessageID DHCP6_PACKET_DROP_DHCP_DISABLED = "DHCP6_PACKET_DROP_DHCP_DISABLED";
extern const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS = "DHCP6_PACKET_DROP_DROP_CLASS";
extern const isc::log::MessageID DHCP6_PACKET_DROP_PARSE_FAIL = "DHCP6_PACKET_DROP_PARSE_FAIL";
extern const isc::log::MessageID DHCP6_PACKET_DROP_SERVERID_MISMATCH = "DHCP6_PACKET_DROP_SERVERID_MISMATCH";
extern const isc::log::MessageID DHCP6_PACKET_DROP_UNICAST = "DHCP6_PACKET_DROP_UNICAST";
......@@ -230,6 +231,7 @@ const char* values[] = {
"DHCP6_OPEN_SOCKET", "opening service sockets on port %1",
"DHCP6_OPEN_SOCKET_FAIL", "failed to open socket: %1",
"DHCP6_PACKET_DROP_DHCP_DISABLED", "%1: DHCP service is globally disabled",
"DHCP6_PACKET_DROP_DROP_CLASS", "dropped as member of the special DROP class: %1",
"DHCP6_PACKET_DROP_PARSE_FAIL", "failed to parse packet from %1 to %2, received over interface %3, reason: %4",
"DHCP6_PACKET_DROP_SERVERID_MISMATCH", "%1: dropping packet with server identifier: %2, server is using: %3",
"DHCP6_PACKET_DROP_UNICAST", "%1: dropping unicast %2 packet as this packet should be sent to multicast",
......
......@@ -89,6 +89,7 @@ extern const isc::log::MessageID DHCP6_NO_SOCKETS_OPEN;
extern const isc::log::MessageID DHCP6_OPEN_SOCKET;
extern const isc::log::MessageID DHCP6_OPEN_SOCKET_FAIL;
extern const isc::log::MessageID DHCP6_PACKET_DROP_DHCP_DISABLED;
extern const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS;
extern const isc::log::MessageID DHCP6_PACKET_DROP_PARSE_FAIL;
extern const isc::log::MessageID DHCP6_PACKET_DROP_SERVERID_MISMATCH;
extern const isc::log::MessageID DHCP6_PACKET_DROP_UNICAST;
......
......@@ -505,6 +505,10 @@ has been temporarily disabled. This affects all received DHCP packets. The
service may be enabled by the "dhcp-enable" control command or automatically
after a specified amount of time since receiving "dhcp-disable" command.
% DHCP6_PACKET_DROP_DROP_CLASS dropped as member of the special DROP class: %1
This informational message is emitted when an incoming packet was classified
into the special DROP class and dropped. The packet details are displayed.
% DHCP6_PACKET_DROP_PARSE_FAIL failed to parse packet from %1 to %2, received over interface %3, reason: %4
The DHCPv6 server has received a packet that it is unable to
interpret. The reason why the packet is invalid is included in the message.
......
......@@ -683,6 +683,14 @@ Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
return;
}
// Check the DROP special class.
if (query->inClass("DROP")) {
LOG_INFO(packet6_logger, DHCP6_PACKET_DROP_DROP_CLASS)
.arg(query->toText());
// increase pkt6-receive-drop stats?
return;
}
if (query->getType() == DHCPV6_DHCPV4_QUERY) {
// This call never throws. Should this change, this section must be
// enclosed in try-catch.
......
// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -67,6 +67,12 @@ namespace {
/// option 1234 'foo' aka telephones
/// option 1234 'bar' aka computers
///
/// - Configuration 3:
/// - Used for the DROP class
/// - 1 subnet: 2001:db8:1::/48
/// - 2 pool: 2001:db8:1:1::/64
/// - the following class defined: option 1234 'foo', DROP
///
const char* CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
......@@ -249,8 +255,38 @@ const char* CONFIGS[] = {
" \"prefix-len\": 48, \"delegated-len\": 64,"
" \"client-class\": \"server2_and_computers\" } ]"
" } ],"
"\"valid-lifetime\": 4000 }"
"\"valid-lifetime\": 4000 }",
// Configuration 3
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"option-def\": [ "
"{"
" \"name\": \"host-name\","
" \"code\": 1234,"
" \"type\": \"string\""
"},"
"{"
" \"name\": \"ipv6-forwarding\","
" \"code\": 2345,"
" \"type\": \"boolean\""
"} ],"
"\"client-classes\": ["
"{"
" \"name\": \"DROP\","
" \"test\": \"option[host-name].text == 'foo'\""
"}"
"],"
"\"subnet6\": [ "
"{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], "
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth1\""
" } ],"
"\"valid-lifetime\": 4000 }"
};
/// @brief Test fixture class for testing client classification by the
......@@ -2042,4 +2078,35 @@ TEST_F(ClassifyTest, pDserver2Computer) {
EXPECT_EQ("2001:db8:4::", lease_client.addr_.toText());
}
// This test checks the handling for the DROP special class.
TEST_F(ClassifyTest, dropClass) {
Dhcp6Client client;
client.setDUID("01:02:03:05");
client.setInterface("eth1");
client.requestAddress();
// Configure DHCP server.
ASSERT_NO_THROW(configure(CONFIGS[3], *client.getServer()));
// Send a message to the server.
ASSERT_NO_THROW(client.doSolicit(true));
// No option: no drop.
EXPECT_TRUE(client.getContext().response_);
// Retry with an option matching the DROP class.
Dhcp6Client client2;
// Add the host-name option.
OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
ASSERT_TRUE(hostname);
client2.addExtraOption(hostname);
// Send a message to the server.
ASSERT_NO_THROW(client2.doSolicit(true));
// Option, dropped.
EXPECT_FALSE(client2.getContext().response_);
}
} // end of anonymous namespace
// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -341,6 +341,10 @@ ClientClassDictionary::toElement() const {
std::list<std::string>
builtinNames = {
// DROP is not in this list because it is special but not built-in.
// In fact DROP is set from an expression as callouts can drop
// directly the incoming packet. The expression must not depend on
// KNOWN/UNKNOWN which are set far after the drop point.
"ALL", "KNOWN", "UNKNOWN"
};
......
// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -382,7 +382,7 @@ private:
typedef boost::shared_ptr<ClientClassDictionary> ClientClassDictionaryPtr;
/// @brief List of built-in client class names.
/// i.e. ALL, KNOWN and UNKNOWN.
/// i.e. ALL, KNOWN and UNKNOWN but not DROP.
extern std::list<std::string> builtinNames;
/// @brief List of built-in client class prefixes
......
......@@ -203,6 +203,32 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
}
// Sanity checks on built-in classes
for (auto bn : builtinNames) {
if (name == bn) {
if (required) {
isc_throw(DhcpConfigError, "built-in class '" << name
<< "' only-if-required flag must be false");
}
if (!test.empty()) {
isc_throw(DhcpConfigError, "built-in class '" << name
<< "' test expression must be empty");
}
}
}
// Sanity checks on DROP
if (name == "DROP") {
if (required) {
isc_throw(DhcpConfigError, "special class '" << name
<< "' only-if-required flag must be false");
}
if (depend_on_known) {
isc_throw(DhcpConfigError, "special class '" << name
<< "' must not depend on 'KNOWN'/'UNKNOWN' classes");
}
}
// Add the client class definition
try {
class_dictionary->addClass(name, match_expr, test, required,
......
// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -1185,4 +1185,110 @@ TEST_F(ClientClassDefListParserTest, dependOnKnown) {
EXPECT_TRUE(cclass->getDependOnKnown());
}
// Verifies that a built-in class can't be required or evaluated.
TEST_F(ClientClassDefListParserTest, builtinCheckError) {
std::string cfg_text =
"[ \n"
" { \n"
" \"name\": \"ALL\" \n"
" } \n"
"] \n";
EXPECT_NO_THROW(parseClientClassDefList(cfg_text, AF_INET6));
cfg_text =
"[ \n"
" { \n"
" \"name\": \"ALL\", \n"
" \"only-if-required\": true \n"
" } \n"
"] \n";
EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET), DhcpConfigError);
cfg_text =
"[ \n"
" { \n"
" \"name\": \"ALL\", \n"
" \"test\": \"'aa' == 'aa'\" \n"
" } \n"
"] \n";
EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET6), DhcpConfigError);
cfg_text =
"[ \n"
" { \n"
" \"name\": \"KNOWN\", \n"
" \"only-if-required\": true \n"
" } \n"
"] \n";
EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET), DhcpConfigError);
cfg_text =
"[ \n"
" { \n"
" \"name\": \"KNOWN\", \n"
" \"test\": \"'aa' == 'aa'\" \n"
" } \n"
"] \n";
EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET6), DhcpConfigError);
cfg_text =
"[ \n"
" { \n"
" \"name\": \"UNKNOWN\", \n"
" \"only-if-required\": true \n"
" } \n"
"] \n";
EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET), DhcpConfigError);
cfg_text =
"[ \n"
" { \n"
" \"name\": \"UNKNOWN\", \n"
" \"test\": \"'aa' == 'aa'\" \n"
" } \n"
"] \n";
EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET6), DhcpConfigError);
}
// Verifies that the special DROP class can't be required or
// dependent on KNOWN/UNKNOWN
TEST_F(ClientClassDefListParserTest, dropCheckError) {
std::string cfg_text =
"[ \n"
" { \n"
" \"name\": \"DROP\", \n"
" \"test\": \"option[123].text == 'abc'\" \n"
" } \n"
"] \n";
EXPECT_NO_THROW(parseClientClassDefList(cfg_text, AF_INET6));
cfg_text =
"[ \n"
" { \n"
" \"name\": \"DROP\", \n"
" \"only-if-required\": true \n"
" } \n"
"] \n";
EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET), DhcpConfigError);
cfg_text =
"[ \n"
" { \n"
" \"name\": \"DROP\", \n"
" \"test\": \"member('KNOWN')\" \n"
" } \n"
"] \n";
EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET6), DhcpConfigError);
}
} // end of anonymous namespace
Markdown is supported
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