]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3588] Modified no test required classes
authorFrancis Dupont <fdupont@isc.org>
Tue, 8 Oct 2024 14:10:13 +0000 (16:10 +0200)
committerFrancis Dupont <fdupont@isc.org>
Fri, 11 Oct 2024 08:18:01 +0000 (10:18 +0200)
14 files changed:
changelog_unreleased/3588-required-no-test [new file with mode: 0644]
changelog_unreleased/3590-required-precedence [moved from changelog_unreleased/3590-required-precence with 100% similarity]
doc/sphinx/arm/dhcp4-srv.rst
doc/sphinx/arm/dhcp6-srv.rst
src/bin/dhcp4/dhcp4_messages.cc
src/bin/dhcp4/dhcp4_messages.h
src/bin/dhcp4/dhcp4_messages.mes
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/tests/classify_unittest.cc
src/bin/dhcp6/dhcp6_messages.cc
src/bin/dhcp6/dhcp6_messages.h
src/bin/dhcp6/dhcp6_messages.mes
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/tests/classify_unittest.cc

diff --git a/changelog_unreleased/3588-required-no-test b/changelog_unreleased/3588-required-no-test
new file mode 100644 (file)
index 0000000..0f2b9e0
--- /dev/null
@@ -0,0 +1,6 @@
+[func]*                fdupont
+       Modified the behavior of required client classes
+       configured without a test expression: they are now
+       unconditionally added as they always evaluate to
+       true (vs false previously).
+       (Gitlab #3388)
index 3eb08147cd74a1ad47df8ad4677868daff66535a..b0a30cceca5125ed30ad9fd5b0ca387f1cf4bb9b 100644 (file)
@@ -3423,6 +3423,10 @@ The order in which required classes are considered is: pool, subnet,
 and shared network, i.e. in the same order from the way in which
 ``option-data`` is processed.
 
+Since Kea version 2.7.4 required client classes configured without
+a test expression are unconditionally added, i.e. they are considered
+to always be evaluated to ``true``.
+
 .. note::
 
    Vendor-Identifying Vendor Options are a special case: for all other
index 29bc86481f5b87c4427710c5561fc2fa25878630..1f03f70a16c70fc4c08c871c977e8d31f071c474 100644 (file)
@@ -3201,6 +3201,10 @@ levels. The order in which required classes are considered is:
 (pd-)pool, subnet, and shared network, i.e. in the same order from the
 way in which ``option-data`` is processed.
 
+Since Kea version 2.7.4 required client classes configured without
+a test expression are unconditionally added, i.e. they are considered
+to always be evaluated to ``true``.
+
 .. _dhcp6-ddns-config:
 
 DDNS for DHCPv6
index 9d4cedc5e40a94a39e815e80ef9eb07aaebe6435..81d191c9254af1e0d9ce4bf4376e0171c9311dd1 100644 (file)
@@ -19,8 +19,6 @@ extern const isc::log::MessageID DHCP4_CLASSES_ASSIGNED = "DHCP4_CLASSES_ASSIGNE
 extern const isc::log::MessageID DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION = "DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION";
 extern const isc::log::MessageID DHCP4_CLASS_ASSIGNED = "DHCP4_CLASS_ASSIGNED";
 extern const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED = "DHCP4_CLASS_UNCONFIGURED";
-extern const isc::log::MessageID DHCP4_CLASS_UNDEFINED = "DHCP4_CLASS_UNDEFINED";
-extern const isc::log::MessageID DHCP4_CLASS_UNTESTABLE = "DHCP4_CLASS_UNTESTABLE";
 extern const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES = "DHCP4_CLIENTID_IGNORED_FOR_LEASES";
 extern const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA = "DHCP4_CLIENT_FQDN_DATA";
 extern const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS = "DHCP4_CLIENT_FQDN_PROCESS";
@@ -150,6 +148,8 @@ extern const isc::log::MessageID DHCP4_RELEASE_FAIL_WRONG_CLIENT = "DHCP4_RELEAS
 extern const isc::log::MessageID DHCP4_REQUEST = "DHCP4_REQUEST";
 extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_EVAL_ERROR = "DHCP4_REQUIRED_CLASS_EVAL_ERROR";
 extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_EVAL_RESULT = "DHCP4_REQUIRED_CLASS_EVAL_RESULT";
+extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNDEFINED = "DHCP4_REQUIRED_CLASS_UNDEFINED";
+extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNTESTABLE = "DHCP4_REQUIRED_CLASS_UNTESTABLE";
 extern const isc::log::MessageID DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED = "DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED";
 extern const isc::log::MessageID DHCP4_RESERVED_HOSTNAME_ASSIGNED = "DHCP4_RESERVED_HOSTNAME_ASSIGNED";
 extern const isc::log::MessageID DHCP4_RESPONSE_DATA = "DHCP4_RESPONSE_DATA";
@@ -197,8 +197,6 @@ const char* values[] = {
     "DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION", "%1: client packet has been assigned to the following classes: %2",
     "DHCP4_CLASS_ASSIGNED", "%1: client packet has been assigned to the following class: %2",
     "DHCP4_CLASS_UNCONFIGURED", "%1: client packet belongs to an unconfigured class: %2",
-    "DHCP4_CLASS_UNDEFINED", "required class %1 has no definition",
-    "DHCP4_CLASS_UNTESTABLE", "required class %1 has no test expression",
     "DHCP4_CLIENTID_IGNORED_FOR_LEASES", "%1: not using client identifier for lease allocation for subnet %2",
     "DHCP4_CLIENT_FQDN_DATA", "%1: Client sent FQDN option: %2",
     "DHCP4_CLIENT_FQDN_PROCESS", "%1: processing Client FQDN option",
@@ -328,6 +326,8 @@ const char* values[] = {
     "DHCP4_REQUEST", "%1: server is processing DHCPREQUEST with hint=%2",
     "DHCP4_REQUIRED_CLASS_EVAL_ERROR", "%1: Expression '%2' evaluated to %3",
     "DHCP4_REQUIRED_CLASS_EVAL_RESULT", "%1: Expression '%2' evaluated to %3",
+    "DHCP4_REQUIRED_CLASS_UNDEFINED", "required class %1 has no definition",
+    "DHCP4_REQUIRED_CLASS_UNTESTABLE", "required class %1 has no test expression",
     "DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED", "Multi-threading is enabled and host reservations lookup is always performed first.",
     "DHCP4_RESERVED_HOSTNAME_ASSIGNED", "%1: server assigned reserved hostname %2",
     "DHCP4_RESPONSE_DATA", "%1: responding with packet %2 (type %3), packet details: %4",
index 2005b255f4d110cda8ab7e18c543953ea716949b..00e60df071236635614974b2926e5daef2af8108 100644 (file)
@@ -20,8 +20,6 @@ extern const isc::log::MessageID DHCP4_CLASSES_ASSIGNED;
 extern const isc::log::MessageID DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION;
 extern const isc::log::MessageID DHCP4_CLASS_ASSIGNED;
 extern const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED;
-extern const isc::log::MessageID DHCP4_CLASS_UNDEFINED;
-extern const isc::log::MessageID DHCP4_CLASS_UNTESTABLE;
 extern const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES;
 extern const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA;
 extern const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS;
@@ -151,6 +149,8 @@ extern const isc::log::MessageID DHCP4_RELEASE_FAIL_WRONG_CLIENT;
 extern const isc::log::MessageID DHCP4_REQUEST;
 extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_EVAL_ERROR;
 extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_EVAL_RESULT;
+extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNDEFINED;
+extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNTESTABLE;
 extern const isc::log::MessageID DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED;
 extern const isc::log::MessageID DHCP4_RESERVED_HOSTNAME_ASSIGNED;
 extern const isc::log::MessageID DHCP4_RESPONSE_DATA;
index 98a4609bb3ada65dce06d306f796a34b8a35993f..41cb5652a4fe74e76222ee51be53a19cbe9ccfe3 100644 (file)
@@ -94,16 +94,6 @@ 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_CLASS_UNDEFINED required class %1 has no definition
-Logged at debug log level 40.
-This debug message informs that a class is listed for required evaluation but
-has no definition.
-
-% DHCP4_CLASS_UNTESTABLE required class %1 has no test expression
-Logged at debug log level 40.
-This debug message informs that a class was listed for required evaluation but
-its definition does not include a test expression to evaluate.
-
 % DHCP4_CLIENTID_IGNORED_FOR_LEASES %1: not using client identifier for lease allocation for subnet %2
 Logged at debug log level 50.
 This debug message is issued when the server is processing the DHCPv4 message
@@ -989,6 +979,17 @@ This debug message indicates that the expression of a required client class has
 been successfully evaluated. The client class name and the result value of the
 evaluation are printed.
 
+% DHCP4_REQUIRED_CLASS_UNDEFINED required class %1 has no definition
+Logged at debug log level 40.
+This debug message informs that a class is listed for required evaluation but
+has no definition. The class is ignored.
+
+% DHCP4_REQUIRED_CLASS_UNTESTABLE required class %1 has no test expression
+Logged at debug log level 40.
+This debug message informs that a class was listed for required evaluation but
+its definition does not include a test expression to evaluate. The class is
+unconditionally added to the query.
+
 % DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED Multi-threading is enabled and host reservations lookup is always performed first.
 This is a message informing that host reservations lookup is performed before
 lease lookup when multi-threading is enabled overwriting configured value.
index 107874f12b5b8b7488bd733832bc4406f697b489..7dc53314abc01c791205d41a8725cee126e3fa36 100644 (file)
@@ -4866,15 +4866,19 @@ void Dhcpv4Srv::requiredClassify(Dhcpv4Exchange& ex) {
     for (auto const& cclass : classes) {
         const ClientClassDefPtr class_def = dict->findClass(cclass);
         if (!class_def) {
-            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNDEFINED)
+            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
+                      DHCP4_REQUIRED_CLASS_UNDEFINED)
                 .arg(cclass);
+            // Ignore it as it can't have an attached action
             continue;
         }
         const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
-        // Nothing to do without an expression to evaluate
+        // Add a class without an expression to evaluate
         if (!expr_ptr) {
-            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNTESTABLE)
+            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
+                      DHCP4_REQUIRED_CLASS_UNTESTABLE)
                 .arg(cclass);
+            query->addClass(cclass);
             continue;
         }
         // Evaluate the expression which can return false (no match),
index b8bf0324976b737922f5b8ea078a1026ad1978d1..a0fe8bff1d8fd933c6287dad9611a980cd4b1d2a 100644 (file)
@@ -1238,6 +1238,100 @@ TEST_F(ClassifyTest, precedenceNetwork) {
     EXPECT_EQ("10.0.0.3", addrs[0].toText());
 }
 
+// This test checks a required class without a test entry can be
+// unconditionally added.
+TEST_F(ClassifyTest, requiredNoTest) {
+    std::string config =
+        "{"
+        "\"interfaces-config\": {"
+        "   \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"client-classes\": ["
+        "    {"
+        "       \"name\": \"for-network\","
+        "       \"option-data\": [ {"
+        "           \"name\": \"domain-name-servers\","
+        "           \"data\": \"10.0.0.3\""
+        "       } ]"
+        "    }"
+        "],"
+        "\"shared-networks\": [ {"
+        "    \"name\": \"frog\","
+        "    \"require-client-classes\": [ \"for-network\" ],"
+        "    \"subnet4\": [ { "
+        "        \"subnet\": \"10.0.0.0/24\","
+        "        \"id\": 1,"
+        "        \"pools\": [ { "
+        "            \"pool\": \"10.0.0.10-10.0.0.100\""
+        "         } ]"
+        "    } ]"
+        "} ]"
+        "}";
+
+    // Create a client requesting domain-name-servers option
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS);
+
+    // Load the config and perform a DORA
+    configure(config, *client.getServer());
+    ASSERT_NO_THROW(client.doDORA());
+
+    // Check response
+    Pkt4Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+    EXPECT_EQ("10.0.0.10", resp->getYiaddr().toText());
+
+    // Check domain-name-servers option
+    OptionPtr opt = resp->getOption(DHO_DOMAIN_NAME_SERVERS);
+    ASSERT_TRUE(opt);
+    Option4AddrLstPtr servers =
+        boost::dynamic_pointer_cast<Option4AddrLst>(opt);
+    ASSERT_TRUE(servers);
+    auto addrs = servers->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    EXPECT_EQ("10.0.0.3", addrs[0].toText());
+}
+
+// This test checks a required class which is not defined is ignored.
+TEST_F(ClassifyTest, requiredNoDefined) {
+    std::string config =
+        "{"
+        "\"interfaces-config\": {"
+        "   \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"shared-networks\": [ {"
+        "    \"name\": \"frog\","
+        "    \"require-client-classes\": [ \"for-network\" ],"
+        "    \"subnet4\": [ { "
+        "        \"subnet\": \"10.0.0.0/24\","
+        "        \"id\": 1,"
+        "        \"pools\": [ { "
+        "            \"pool\": \"10.0.0.10-10.0.0.100\""
+        "         } ]"
+        "    } ]"
+        "} ]"
+        "}";
+
+    // Create a client requesting domain-name-servers option
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS);
+
+    // Load the config and perform a DORA
+    configure(config, *client.getServer());
+    ASSERT_NO_THROW(client.doDORA());
+
+    // Check response
+    Pkt4Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+    EXPECT_EQ("10.0.0.10", resp->getYiaddr().toText());
+
+    // Check domain-name-servers option
+    OptionPtr opt = resp->getOption(DHO_DOMAIN_NAME_SERVERS);
+    EXPECT_FALSE(opt);
+}
+
 // This test checks the handling for the DROP special class.
 TEST_F(ClassifyTest, dropClass) {
     Dhcp4Client client(Dhcp4Client::SELECTING);
index 3b436d8876674b82ee09b2c41e40bc19e93644bd..af7d80707d585ba5d1aba2e7f140e5bd0e26f018 100644 (file)
@@ -21,8 +21,6 @@ extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED = "DHCP6_CLASSES_ASSIGNE
 extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION = "DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION";
 extern const isc::log::MessageID DHCP6_CLASS_ASSIGNED = "DHCP6_CLASS_ASSIGNED";
 extern const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED = "DHCP6_CLASS_UNCONFIGURED";
-extern const isc::log::MessageID DHCP6_CLASS_UNDEFINED = "DHCP6_CLASS_UNDEFINED";
-extern const isc::log::MessageID DHCP6_CLASS_UNTESTABLE = "DHCP6_CLASS_UNTESTABLE";
 extern const isc::log::MessageID DHCP6_CONFIG_COMPLETE = "DHCP6_CONFIG_COMPLETE";
 extern const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL = "DHCP6_CONFIG_LOAD_FAIL";
 extern const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE = "DHCP6_CONFIG_PACKET_QUEUE";
@@ -149,6 +147,8 @@ extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_DUID = "DHCP6_RELEA
 extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID = "DHCP6_RELEASE_PD_FAIL_WRONG_IAID";
 extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_EVAL_ERROR = "DHCP6_REQUIRED_CLASS_EVAL_ERROR";
 extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_EVAL_RESULT = "DHCP6_REQUIRED_CLASS_EVAL_RESULT";
+extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNDEFINED = "DHCP6_REQUIRED_CLASS_UNDEFINED";
+extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNTESTABLE = "DHCP6_REQUIRED_CLASS_UNTESTABLE";
 extern const isc::log::MessageID DHCP6_REQUIRED_OPTIONS_CHECK_FAIL = "DHCP6_REQUIRED_OPTIONS_CHECK_FAIL";
 extern const isc::log::MessageID DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED = "DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED";
 extern const isc::log::MessageID DHCP6_RESPONSE_DATA = "DHCP6_RESPONSE_DATA";
@@ -188,8 +188,6 @@ const char* values[] = {
     "DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION", "%1: client packet has been assigned to the following classes: %2",
     "DHCP6_CLASS_ASSIGNED", "%1: client packet has been assigned to the following class: %2",
     "DHCP6_CLASS_UNCONFIGURED", "%1: client packet belongs to an unconfigured class: %2",
-    "DHCP6_CLASS_UNDEFINED", "required class %1 has no definition",
-    "DHCP6_CLASS_UNTESTABLE", "required class %1 has no test expression",
     "DHCP6_CONFIG_COMPLETE", "DHCPv6 server has completed configuration: %1",
     "DHCP6_CONFIG_LOAD_FAIL", "configuration error using file: %1, reason: %2",
     "DHCP6_CONFIG_PACKET_QUEUE", "DHCPv6 packet queue info after configuration: %1",
@@ -316,6 +314,8 @@ const char* values[] = {
     "DHCP6_RELEASE_PD_FAIL_WRONG_IAID", "%1: client tried to release prefix %2/%3, but it used wrong IAID (expected %4, but got %5)",
     "DHCP6_REQUIRED_CLASS_EVAL_ERROR", "%1: Expression '%2' evaluated to %3",
     "DHCP6_REQUIRED_CLASS_EVAL_RESULT", "%1: Expression '%2' evaluated to %3",
+    "DHCP6_REQUIRED_CLASS_UNDEFINED", "required class %1 has no definition",
+    "DHCP6_REQUIRED_CLASS_UNTESTABLE", "required class %1 has no test expression",
     "DHCP6_REQUIRED_OPTIONS_CHECK_FAIL", "%1: %2 message received from %3 failed the following check: %4",
     "DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED", "Multi-threading is enabled and host reservations lookup is always performed first.",
     "DHCP6_RESPONSE_DATA", "%1: responding with packet %2 (type %3), packet details: %4",
index dcd9a5af4789238da9647b481c5ce1e7473ad92e..2faa9cee776c113114885b7eb496544d7f6cea68 100644 (file)
@@ -22,8 +22,6 @@ extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED;
 extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION;
 extern const isc::log::MessageID DHCP6_CLASS_ASSIGNED;
 extern const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED;
-extern const isc::log::MessageID DHCP6_CLASS_UNDEFINED;
-extern const isc::log::MessageID DHCP6_CLASS_UNTESTABLE;
 extern const isc::log::MessageID DHCP6_CONFIG_COMPLETE;
 extern const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL;
 extern const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE;
@@ -150,6 +148,8 @@ extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_DUID;
 extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID;
 extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_EVAL_ERROR;
 extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_EVAL_RESULT;
+extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNDEFINED;
+extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNTESTABLE;
 extern const isc::log::MessageID DHCP6_REQUIRED_OPTIONS_CHECK_FAIL;
 extern const isc::log::MessageID DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED;
 extern const isc::log::MessageID DHCP6_RESPONSE_DATA;
index 3e3a08836e0bc701e9468808c7900cb9f1e784bb..6fb9072a7dfc8707180a62db57cbbe18e3d76a2a 100644 (file)
@@ -112,16 +112,6 @@ 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_CLASS_UNDEFINED required class %1 has no definition
-Logged at debug log level 40.
-This debug message informs that a class is listed for required evaluation but
-has no definition.
-
-% DHCP6_CLASS_UNTESTABLE required class %1 has no test expression
-Logged at debug log level 40.
-This debug message informs that a class was listed for required evaluation but
-its definition does not include a test expression to evaluate.
-
 % DHCP6_CONFIG_COMPLETE DHCPv6 server has completed configuration: %1
 This is an informational message announcing the successful processing of a
 new configuration. it is output during server startup, and when an updated
@@ -994,6 +984,17 @@ This debug message indicates that the expression of a required client class has
 been successfully evaluated. The client class name and the result value of the
 evaluation are printed.
 
+% DHCP6_REQUIRED_CLASS_UNDEFINED required class %1 has no definition
+Logged at debug log level 40.
+This debug message informs that a class is listed for required evaluation but
+has no definition. The class is ignored.
+
+% DHCP6_REQUIRED_CLASS_UNTESTABLE required class %1 has no test expression
+Logged at debug log level 40.
+This debug message informs that a class was listed for required evaluation but
+its definition does not include a test expression to evaluate. The class is
+unconditionally added to the query.
+
 % DHCP6_REQUIRED_OPTIONS_CHECK_FAIL %1: %2 message received from %3 failed the following check: %4
 Logged at debug log level 40.
 This message indicates that received DHCPv6 packet is invalid.  This may be due
index 6df646060387f81c094a651f2cc3bfb05557e6a5..ee4841e16e02e862a0f610f40a6f3951f5f9633d 100644 (file)
@@ -4495,15 +4495,19 @@ Dhcpv6Srv::requiredClassify(const Pkt6Ptr& pkt, AllocEngine::ClientContext6& ctx
     for (auto const& cclass : classes) {
         const ClientClassDefPtr class_def = dict->findClass(cclass);
         if (!class_def) {
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_UNDEFINED)
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC,
+                      DHCP6_REQUIRED_CLASS_UNDEFINED)
                 .arg(cclass);
+            // Ignore it as it can't have an attached action
             continue;
         }
         const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
-        // Nothing to do without an expression to evaluate
+        // Add a class without an expression to evaluate
         if (!expr_ptr) {
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_UNTESTABLE)
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC,
+                      DHCP6_REQUIRED_CLASS_UNTESTABLE)
                 .arg(cclass);
+            pkt->addClass(cclass);
             continue;
         }
         // Evaluate the expression which can return false (no match),
index 2892076abaf1b6f9d01a3cfa104b76f170f324b0..a090f84406df395a3d5f92df712b7211be9d220a 100644 (file)
@@ -2268,6 +2268,106 @@ TEST_F(ClassifyTest, precedenceNetwork) {
     EXPECT_EQ("2001:db8:1::3", addrs[0].toText());
 }
 
+// This test checks a required class without a test entry can be
+// unconditionally added.
+TEST_F(ClassifyTest, requiredNoTest) {
+    std::string config =
+        "{"
+        "\"interfaces-config\": {"
+        "   \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"client-classes\": ["
+        "    {"
+        "       \"name\": \"for-network\","
+        "       \"option-data\": [ {"
+        "           \"name\": \"dns-servers\","
+        "           \"data\": \"2001:db8:1::3\""
+        "       } ]"
+        "    }"
+        "],"
+        "\"shared-networks\": [ {"
+        "    \"name\": \"frog\","
+        "    \"interface\": \"eth1\","
+        "    \"require-client-classes\": [ \"for-network\" ],"
+        "    \"subnet6\": [ { "
+        "        \"subnet\": \"2001:db8:1::/64\","
+        "        \"id\": 1,"
+        "        \"pools\": [ { "
+        "            \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\""
+        "        } ]"
+        "    } ]"
+        "} ],"
+        "\"valid-lifetime\": 600"
+        "}";
+
+    // Create a client requesting dns-servers option
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+    client.requestOption(D6O_NAME_SERVERS);
+
+    // Load the config and perform a SARR
+    configure(config, *client.getServer());
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Check response
+    EXPECT_EQ(1, client.getLeaseNum());
+    Pkt6Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+
+    // Check dns-servers option
+    OptionPtr opt = resp->getOption(D6O_NAME_SERVERS);
+    ASSERT_TRUE(opt);
+    Option6AddrLstPtr servers =
+        boost::dynamic_pointer_cast<Option6AddrLst>(opt);
+    ASSERT_TRUE(servers);
+    auto addrs = servers->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    EXPECT_EQ("2001:db8:1::3", addrs[0].toText());
+}
+
+// This test checks a required class which is not defined is ignored.
+TEST_F(ClassifyTest, requiredNoDefined) {
+    std::string config =
+        "{"
+        "\"interfaces-config\": {"
+        "   \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"shared-networks\": [ {"
+        "    \"name\": \"frog\","
+        "    \"interface\": \"eth1\","
+        "    \"require-client-classes\": [ \"for-network\" ],"
+        "    \"subnet6\": [ { "
+        "        \"subnet\": \"2001:db8:1::/64\","
+        "        \"id\": 1,"
+        "        \"pools\": [ { "
+        "            \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\""
+        "        } ]"
+        "    } ]"
+        "} ],"
+        "\"valid-lifetime\": 600"
+        "}";
+
+    // Create a client requesting dns-servers option
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+    client.requestOption(D6O_NAME_SERVERS);
+
+    // Load the config and perform a SARR
+    configure(config, *client.getServer());
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Check response
+    EXPECT_EQ(1, client.getLeaseNum());
+    Pkt6Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+
+    // Check dns-servers option
+    OptionPtr opt = resp->getOption(D6O_NAME_SERVERS);
+    EXPECT_FALSE(opt);
+}
+
 // This test checks the complex membership from HA with server1 telephone.
 TEST_F(ClassifyTest, server1Telephone) {
     // Create a client.