diff --git a/ChangeLog b/ChangeLog
index b3956c5ebe36abc199efa453e735e3dd1abe4142..7db72f495b6265e7ab4d1f1dbce0afd14a1b7f6f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+1621. [func] fdupont
+ Both kea-dhcp4 and kea-dhcp6 now support a special class, 'DROP'.
+ When the class is defined, inbound client packets that match the
+ class's match expression will be dropped without further processing.
+ Each such drop is logged at DEBUG level and accounted for in
+ drop statistics.
+ (Gitlab #606,!375, git bfa5b2c50324e9d2339daa8309774f49a5e7bf3c)
+
1620. [func] franek, razvan
Kea statistics improvements: Support for storing more than one
sample.
diff --git a/doc/guide/classify.xml b/doc/guide/classify.xml
index 4755913c89ce7e7960e20c7bd06bd11101f984cd..83f51fdd2eb36bd8900a9f8cc9245d42481df482 100644
--- a/doc/guide/classify.xml
+++ b/doc/guide/classify.xml
@@ -90,6 +90,11 @@
resort) definition.
+ When the incoming packet belongs the special class, "DROP", it is
+ dropped and an informational message is logged with the packet
+ information.
+
+
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.
If needed, addresses and prefixes from pools are assigned,
diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml
index 1e9b5384823436a31f60df3176efbc16fbe6a1ac..880e1e621a414acceb2be9f77a1d9a1cb3653494 100644
--- a/doc/guide/dhcp4-srv.xml
+++ b/doc/guide/dhcp4-srv.xml
@@ -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 special
+ class, "DROP", it is dropped and a debug 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.
diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml
index c17bd8859110112b3a8587cbf42a2570238fc7a8..538ed188e694f6407cdea1f9dadfea3833a77707 100644
--- a/doc/guide/dhcp6-srv.xml
+++ b/doc/guide/dhcp6-srv.xml
@@ -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 in the special
+ class, "DROP", it is dropped and a debug 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
diff --git a/src/bin/dhcp4/dhcp4_messages.cc b/src/bin/dhcp4/dhcp4_messages.cc
index 44c0f7538a01b25a6939a6b73e565f7f04c68516..25604b128a30891fe1ddb6fa533f264f4f00936e 100644
--- a/src/bin/dhcp4/dhcp4_messages.cc
+++ b/src/bin/dhcp4/dhcp4_messages.cc
@@ -1,4 +1,4 @@
-// File created from ../../../src/bin/dhcp4/dhcp4_messages.mes on Thu Jun 27 2019 19:56
+// File created from ../../../src/bin/dhcp4/dhcp4_messages.mes on Tue Jul 16 2019 11:03
#include
#include
@@ -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_0010 = "DHCP4_PACKET_DROP_0010";
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_0010", "dropped as member of the special class 'DROP': %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",
diff --git a/src/bin/dhcp4/dhcp4_messages.h b/src/bin/dhcp4/dhcp4_messages.h
index b8c7ff185c1f236eaf1d4ba8218fa6aea7181699..e749541dfa92ccb400d598182c146e7825e0e09e 100644
--- a/src/bin/dhcp4/dhcp4_messages.h
+++ b/src/bin/dhcp4/dhcp4_messages.h
@@ -1,4 +1,4 @@
-// File created from ../../../src/bin/dhcp4/dhcp4_messages.mes on Thu Jun 27 2019 19:56
+// File created from ../../../src/bin/dhcp4/dhcp4_messages.mes on Tue Jul 16 2019 11:03
#ifndef DHCP4_MESSAGES_H
#define DHCP4_MESSAGES_H
@@ -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_0010;
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;
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 40a02f109b8208299b560097fd247ab258397c62..b530ad191fa20a84696e86a3cb476e6e14af8cbe 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -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_0010 dropped as member of the special class 'DROP': %1
+This debug message is emitted when an incoming packet was classified
+into the special class 'DROP' 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
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index d434691c732c5a7dc98d4e414e11c552c14bb0f1..c406508c7a0bda58ef2c9c873fc0510d3c7c2eb4 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -1006,6 +1006,15 @@ 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_DEBUG(packet4_logger, DBGLVL_TRACE_BASIC, DHCP4_PACKET_DROP_0010)
+ .arg(query->toText());
+ isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
+ static_cast(1));
+ return;
+ }
+
AllocEngine::ClientContext4Ptr ctx;
try {
diff --git a/src/bin/dhcp4/tests/classify_unittest.cc b/src/bin/dhcp4/tests/classify_unittest.cc
index 713fd8f4dfb4fc1325aec5920d6bf63bbd9447bd..644b42702079d4fa93fde30619326b8bd9e06062 100644
--- a/src/bin/dhcp4/tests/classify_unittest.cc
+++ b/src/bin/dhcp4/tests/classify_unittest.cc
@@ -1,4 +1,4 @@
-// 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
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
@@ -75,6 +76,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 +282,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 +1130,39 @@ 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(Option::V4, 93, 0x0009));
+ client2.addExtraOption(pxe);
+
+ // Send the discover.
+ client2.doDiscover();
+
+ // Option, dropped.
+ EXPECT_FALSE(client2.getContext().response_);
+
+ // There should also be pkt4-receive-drop stat bumped up.
+ stats::StatsMgr& mgr = stats::StatsMgr::instance();
+ stats::ObservationPtr drop_stat = mgr.getObservation("pkt4-receive-drop");
+
+ // This statistic must be present and must be set to 1.
+ ASSERT_TRUE(drop_stat);
+ EXPECT_EQ(1, drop_stat->getInteger().first);
+}
+
} // end of anonymous namespace
diff --git a/src/bin/dhcp6/dhcp6_messages.cc b/src/bin/dhcp6/dhcp6_messages.cc
index eaab2c015f3ffd11b1acd69d80f40e45d2403c5b..d4e7fc7c72c416c7d1b29219d4628f11215d721c 100644
--- a/src/bin/dhcp6/dhcp6_messages.cc
+++ b/src/bin/dhcp6/dhcp6_messages.cc
@@ -1,4 +1,4 @@
-// File created from ../../../src/bin/dhcp6/dhcp6_messages.mes on Thu Jun 27 2019 20:26
+// File created from ../../../src/bin/dhcp6/dhcp6_messages.mes on Tue Jul 16 2019 11:03
#include
#include
@@ -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 class 'DROP': %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",
diff --git a/src/bin/dhcp6/dhcp6_messages.h b/src/bin/dhcp6/dhcp6_messages.h
index cc328c9d6a798add3a652a5bfa3e94506d65b1e8..ccb62f0f7ff783eaaf812d332a269b3064c9fed2 100644
--- a/src/bin/dhcp6/dhcp6_messages.h
+++ b/src/bin/dhcp6/dhcp6_messages.h
@@ -1,4 +1,4 @@
-// File created from ../../../src/bin/dhcp6/dhcp6_messages.mes on Thu Jun 27 2019 20:26
+// File created from ../../../src/bin/dhcp6/dhcp6_messages.mes on Tue Jul 16 2019 11:03
#ifndef DHCP6_MESSAGES_H
#define DHCP6_MESSAGES_H
@@ -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;
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 0ba507f94b09a6a1f81f70f0eb8c779307ab3568..c980593f617bee4e2c2b942d87b89c9e57a05304 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -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 class 'DROP': %1
+This debug message is emitted when an incoming packet was classified
+into the special class 'DROP' 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.
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 64bb286dd0d94c8770e451b22fdb99e0d406eeb6..c07ab6f311d97fb3eb054bd868e2ee4b1aaa0167 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -683,6 +683,15 @@ Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
return;
}
+ // Check the DROP special class.
+ if (query->inClass("DROP")) {
+ LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_DROP_DROP_CLASS)
+ .arg(query->toText());
+ StatsMgr::instance().addValue("pkt6-receive-drop",
+ static_cast(1));
+ return;
+ }
+
if (query->getType() == DHCPV6_DHCPV4_QUERY) {
// This call never throws. Should this change, this section must be
// enclosed in try-catch.
diff --git a/src/bin/dhcp6/tests/classify_unittests.cc b/src/bin/dhcp6/tests/classify_unittests.cc
index 9620874853349d116198018c320e05c1eebae131..b19372a16d8c3d42e0619dfaa01516cce0bde149 100644
--- a/src/bin/dhcp6/tests/classify_unittests.cc
+++ b/src/bin/dhcp6/tests/classify_unittests.cc
@@ -1,4 +1,4 @@
-// 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
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#include
@@ -67,6 +68,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 +256,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 +2079,43 @@ 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_);
+
+ // There should also be pkt6-receive-drop stat bumped up.
+ stats::StatsMgr& mgr = stats::StatsMgr::instance();
+ stats::ObservationPtr drop_stat = mgr.getObservation("pkt6-receive-drop");
+
+ // This statistic must be present and must be set to 1.
+ ASSERT_TRUE(drop_stat);
+ EXPECT_EQ(1, drop_stat->getInteger().first);
+}
+
} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/client_class_def.cc b/src/lib/dhcpsrv/client_class_def.cc
index 2a9a52098a74e949a5f29e96b2f6dd340cb0e656..bb18d8c328584c5aa42163fee8ce23e77b095412 100644
--- a/src/lib/dhcpsrv/client_class_def.cc
+++ b/src/lib/dhcpsrv/client_class_def.cc
@@ -1,4 +1,4 @@
-// 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
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"
};
diff --git a/src/lib/dhcpsrv/client_class_def.h b/src/lib/dhcpsrv/client_class_def.h
index 94e03c2c56af907fc173b296c8bbae13c804ba02..88425d0a97b69aca149e47571fcaf7506e682532 100644
--- a/src/lib/dhcpsrv/client_class_def.h
+++ b/src/lib/dhcpsrv/client_class_def.h
@@ -1,4 +1,4 @@
-// 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 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 builtinNames;
/// @brief List of built-in client class prefixes
diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc
index 7177a4b7c6dc5fbcc94d8ce5640d697d70f9d510..4be5e85dcf99c07b962894b6b8ac2c845c0b0ee4 100644
--- a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc
+++ b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc
@@ -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,
diff --git a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc
index 0f249311500b9546587458a6c3855f96183c0dd0..813d46d75aee3fa76939fe634ac5b50d73caffd1 100644
--- a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc
+++ b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc
@@ -1,4 +1,4 @@
-// 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