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
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,
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.
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
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";
"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",
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;
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
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
/// 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\": {"
" } ]"
"}",
+ // 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.
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
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";
"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",
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;
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.
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
/// 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\": {"
" \"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
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
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
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
}
+ // 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
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