From a198acc1e2a544d40c2493841e620bdf46683a4d Mon Sep 17 00:00:00 2001 From: Thomas Markwalder Date: Fri, 18 Oct 2024 12:02:04 -0400 Subject: [#3587] Updated doc, messages, parsers, and UTs /doc/sphinx/arm/classify.rst /doc/sphinx/arm/dhcp4-srv.rst /doc/sphinx/arm/dhcp6-srv.rst /doc/sphinx/arm/hooks-class-cmds.rst /doc/sphinx/arm/hooks-limits.rst /src/bin/dhcp4/dhcp4_lexer.ll /src/bin/dhcp4/dhcp4_messages.mes /src/bin/dhcp4/dhcp4_srv.cc /src/bin/dhcp4/tests/classify_unittest.cc /src/bin/dhcp4/tests/config_parser_unittest.cc /src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes /src/bin/dhcp6/dhcp6_srv.cc /src/bin/dhcp6/tests/classify_unittest.cc /src/bin/dhcp6/tests/config_parser_unittest.cc /src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc /src/share/api/class-get.json --- doc/sphinx/arm/classify.rst | 54 +++++++------- doc/sphinx/arm/dhcp4-srv.rst | 57 ++++++++------- doc/sphinx/arm/dhcp6-srv.rst | 53 ++++++++------ doc/sphinx/arm/hooks-class-cmds.rst | 12 +++- doc/sphinx/arm/hooks-limits.rst | 4 +- src/bin/dhcp4/dhcp4_lexer.cc | 2 +- src/bin/dhcp4/dhcp4_lexer.ll | 2 +- src/bin/dhcp4/dhcp4_messages.cc | 16 ++--- src/bin/dhcp4/dhcp4_messages.h | 8 +-- src/bin/dhcp4/dhcp4_messages.mes | 16 ++--- src/bin/dhcp4/dhcp4_srv.cc | 9 ++- src/bin/dhcp4/tests/classify_unittest.cc | 36 +++++----- src/bin/dhcp4/tests/config_parser_unittest.cc | 42 ++++++++--- src/bin/dhcp6/dhcp6_messages.cc | 16 ++--- src/bin/dhcp6/dhcp6_messages.h | 8 +-- src/bin/dhcp6/dhcp6_messages.mes | 18 ++--- src/bin/dhcp6/dhcp6_srv.cc | 8 +-- src/bin/dhcp6/tests/classify_unittest.cc | 84 +++++++++++----------- src/bin/dhcp6/tests/config_parser_unittest.cc | 56 ++++++++++++--- .../tests/client_class_def_parser_unittest.cc | 16 ++++- src/share/api/class-get.json | 2 +- 21 files changed, 310 insertions(+), 209 deletions(-) diff --git a/doc/sphinx/arm/classify.rst b/doc/sphinx/arm/classify.rst index d28fe3aca3..f858224363 100644 --- a/doc/sphinx/arm/classify.rst +++ b/doc/sphinx/arm/classify.rst @@ -736,11 +736,14 @@ A client class definition can contain the following properties: assigned to members of this class. In the case of a template class, these options are assigned to the generated spawned class. - The ``option-def`` list is not mandatory and is used to define custom options. - - The ``only-if-required`` flag is not mandatory; when its value is set to + - The ``only-if-required`` has been replaced with ``only-in-additional-list`` and + is now deprecated. It will still be accepted as input for a time to allow users + to migrate but will eventually be unsupported. + - The ``only-in-additional-list`` flag is not mandatory; when its value is set to ``false`` (the default), membership is determined during classification and is available for subnet selection, for instance. When the value is set to - ``true``, membership is evaluated only when required and is usable only for - option configuration. + ``true``, membership is evaluated only if the class appears in an ``evaluate- + additional-classes`` list and is usable only for option configuration. - The ``user-context`` is not mandatory and represents a map with user-defined data and possibly configuration options for hook libraries. - The ``next-server`` parameter is not mandatory and configures the ``siaddr`` field in @@ -907,18 +910,19 @@ expression for example purposes. Usually the ``test`` and ``template-test`` expressions are evaluated before subnet selection, but in some cases it is useful to evaluate it later when the subnet, shared network, or pools are known but output-option processing has not -yet been done. For this purpose, the ``only-if-required`` flag, which is +yet been done. For this purpose, the ``only-in-additional-list`` flag, which is ``false`` by default, allows the evaluation of the ``test`` expression or the -``template-test`` expression only when it is required, i.e. in a -``require-client-classes`` list of the selected pool, subnet, or shared network. +``template-test`` expression only when it is required by the class's presence +in the ``evaluate-additional-classes`` list of the selected pool, subnet, or +shared network. -The ``require-client-classes`` list, which is valid for pool, subnet, +The ``evaluate-additional-classes`` list, which is valid for pool, subnet, and shared-network scope, specifies the classes which are evaluated in the second pass before output-option processing. The list is built in -same precedence order of the option data, i.e. an option data item in -a subnet takes precedence over one in a shared network, and also a -required class in a subnet is added before one in a shared -network. The mechanism is related to the ``only-if-required`` flag but +same precedence order as the option data, i.e. an option data item in +a subnet takes precedence over one in a shared network. An +additional class in a subnet is added before one in a shared +network. The mechanism is related to the ``only-in-additional-list`` flag but it is not mandatory that the flag be set to ``true``. .. note :: @@ -1200,9 +1204,9 @@ value is obtained is determined as explained in the previous paragraphs. Option Class-Tagging ==================== -Option class-tagging allows an option value to conditionally applied -to the response based on the client's class membership. The effect is -similar to using an if-block in ISC DHCP to conditionally include +Option class-tagging allows an option value to conditionally applied +to the response based on the client's class membership. The effect is +similar to using an if-block in ISC DHCP to conditionally include options at a given scope. Class-tagging is done by specifying a list of one of more class names in the option's ``client-classes`` entry. @@ -1252,24 +1256,24 @@ being added to the response which occurs after lease assignment just before the response is to be sent to the client. -When ``never-send`` for an option is true at any scope, all +When ``never-send`` for an option is true at any scope, all ``client-classes`` entries for that option are ignored. The option will not included. -When ``always-send`` is true at any scope, the option will be -included unless, the option determined by scope specifies -a ``client-classes`` list that does not contain any of the +When ``always-send`` is true at any scope, the option will be +included unless, the option determined by scope specifies +a ``client-classes`` list that does not contain any of the client's classes. -Otherwise, An option requested by the client will be included in +Otherwise, An option requested by the client will be included in the response if the option either does not specify ``client-classes`` or the client belongs to at least one of the classes in ``client-classes``. -When an option's class-tag does not match, it is as though +When an option's class-tag does not match, it is as though the option was not specified at that scope. In the following example the option "foo" is specified for both the subnet and the pool. The pool specification includes a class-tag that limits -the option to members of class "melon": +the option to members of class "melon": :: @@ -1289,16 +1293,16 @@ the option to members of class "melon": }] }] } - + Clients that match class "melon" will have a value of 123 for option "foo", while clients that do not match "melon" will have a value of 456 for option -"foo". +"foo". .. note:: - Though examples above are for DHCPv4, class-tagging syntax and - behavior is the same for DHPCv6. + Though examples above are for DHCPv4, class-tagging syntax and + behavior is the same for DHPCv6. Classes and Hooks ================= diff --git a/doc/sphinx/arm/dhcp4-srv.rst b/doc/sphinx/arm/dhcp4-srv.rst index e23ba5de13..2495db2a13 100644 --- a/doc/sphinx/arm/dhcp4-srv.rst +++ b/doc/sphinx/arm/dhcp4-srv.rst @@ -3421,26 +3421,26 @@ DNS servers set to 192.0.2.1 and 192.0.2.2. ... } -.. _dhcp4-required-class: +.. _dhcp4-additional-class: -Required Classification -~~~~~~~~~~~~~~~~~~~~~~~ +Additional Classification +~~~~~~~~~~~~~~~~~~~~~~~~~ In some cases it is useful to limit the scope of a class to a pool, subnet, or shared network. There are two parameters which are used to limit the scope of the class by instructing the server to evaluate test expressions when required. -The first one is the per-class ``only-if-required`` flag, which is ``false`` -by default. When it is set to ``true``, the test expression of the class -is not evaluated at the reception of the incoming packet but later, and -only if the class evaluation is required. - -The second is ``require-client-classes``, which takes a list of class +The ``evaluate-additional-classes``, which takes a list of class names and is valid in pool, subnet, and shared network scope. Classes in -these lists are marked as required and evaluated after selection of this +these lists are marked as additional and evaluated after selection of this specific pool/subnet/shared network and before output-option processing. +The second one is the per-class ``only-in-additional-list`` flag, which is +``false`` by default. When it is set to ``true``, the test expression of +the class is not evaluated at the reception of the incoming packet but later, +and only if the class is present in an ``evaluate-additional-classes`` list. + In this example, a class is assigned to the incoming packet when the specified subnet is used: @@ -3451,7 +3451,7 @@ specified subnet is used: { "name": "Client_foo", "test": "member('ALL')", - "only-if-required": true + "only-in-additional-list": true }, ... ], @@ -3459,7 +3459,7 @@ specified subnet is used: { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], - "require-client-classes": [ "Client_foo" ], + "evaluate-additional-classes": [ "Client_foo" ], ... }, ... @@ -3467,22 +3467,29 @@ specified subnet is used: ... } -Required evaluation can be used to express complex dependencies like +Additional evaluation can be used to express complex dependencies like subnet membership. It can also be used to reverse the precedence; if ``option-data`` is set in a subnet, it takes precedence over ``option-data`` in a class. If ``option-data`` is moved to a required class and required in the subnet, a class evaluated earlier may take precedence. -Required evaluation is also available at the shared network and pool levels. -The order in which required classes are considered is: pool, subnet, +Additional evaluation is also available at the shared network and pool levels. +The order in which additional 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 +Since Kea version 2.7.4 additional client classes configured without a test expression are unconditionally added, i.e. they are considered to always be evaluated to ``true``. +.. note:: + + As of Kea version 2.7.4, ``only-if-required`` and ``require-client-classes`` + have been renamed to ``only-in-additional-list`` and ``evaluate-additional-classes`` + respectivley. The original names will still be accepted as input to allow + users to migrate but will eventually be unsupported. + .. note:: Vendor-Identifying Vendor Options are a special case: for all other @@ -5366,12 +5373,12 @@ For example: { "name": "dependent-class", "test": "member('KNOWN')", - "only-if-required": true + "only-in-additional-list": true } ] } -The ``only-if-required`` parameter is needed here to force evaluation +The ``only-in-additional-list`` parameter is needed here to force evaluation of the class after the lease has been allocated, and thus the reserved class has been also assigned. @@ -5379,21 +5386,21 @@ class has been also assigned. The classes specified in non-global host reservations are assigned to the processed packet after all classes with the - ``only-if-required`` parameter set to ``false`` have been evaluated. + ``only-in-additional-list`` parameter set to ``false`` have been evaluated. This means that these classes must not depend on the statically assigned classes from the host reservations. If - such a dependency is needed, the ``only-if-required`` parameter must + such a dependency is needed, the ``only-in-additional-list`` parameter must be set to ``true`` for the dependent classes. Such classes are evaluated after the static classes have been assigned to the packet. This, however, imposes additional configuration overhead, because - all classes marked as ``only-if-required`` must be listed in the - ``require-client-classes`` list for every subnet where they are used. + all classes marked as ``only-in-additional-list`` must be listed in the + ``evaluate-additional-classes`` list for every subnet where they are used. .. note:: Client classes specified within the Kea configuration file may depend on the classes specified within the global host reservations. - In such a case the ``only-if-required`` parameter is not needed. + In such a case the ``only-in-additional-list`` parameter is not needed. Refer to :ref:`pool-selection-with-class-reservations4` and :ref:`subnet-selection-with-class-reservations4` for specific use cases. @@ -6806,10 +6813,10 @@ the ``dhcp-server-identifier`` option. This option configuration is only supported at the subnet, shared network, client class, and global levels. It must not be specified at the host-reservation level. When configuring the ``dhcp-server-identifier`` option at client-class level, the -class must not set the ``only-if-required`` flag, because this class would not +class must not set the ``only-in-additional-list`` flag, because this class would not be evaluated before the server determines if the received DHCP message should be accepted for processing. Such classes are evaluated after subnet selection. -See :ref:`dhcp4-required-class` for details. +See :ref:`dhcp4-additional-class` for details. The following example demonstrates how to override the server identifier for a subnet: diff --git a/doc/sphinx/arm/dhcp6-srv.rst b/doc/sphinx/arm/dhcp6-srv.rst index 54827cfd09..90f33167d2 100644 --- a/doc/sphinx/arm/dhcp6-srv.rst +++ b/doc/sphinx/arm/dhcp6-srv.rst @@ -3153,26 +3153,26 @@ eRouter1.0 client class are allowed to use that pool. ... } -.. _dhcp6-required-class: +.. _dhcp6-additional-class: -Required Classification -~~~~~~~~~~~~~~~~~~~~~~~ +Additional Classification +~~~~~~~~~~~~~~~~~~~~~~~~~ In some cases it is useful to limit the scope of a class to a pool, subnet, or shared network. There are two parameters which are used to limit the scope of the class by instructing the server to evaluate test expressions when required. -The first one is the per-class ``only-if-required`` flag, which is ``false`` -by default. When it is set to ``true``, the test expression of the class -is not evaluated at the reception of the incoming packet but later, and -only if the class evaluation is required. - -The second is ``require-client-classes``, which takes a list of class +The ``evaluate-additional-classes``, which takes a list of class names and is valid in pool, subnet, and shared network scope. Classes in -these lists are marked as required and evaluated after selection of this +these lists are marked as additional and evaluated after selection of this specific pool/subnet/shared network and before output-option processing. +The second one is the per-class ``only-in-additional-list`` flag, which is +``false`` by default. When it is set to ``true``, the test expression of +the class is not evaluated at the reception of the incoming packet but later, +and only if the class is present in an ``evaluate-additional-classes`` list. + In this example, a class is assigned to the incoming packet when the specified subnet is used: @@ -3183,7 +3183,7 @@ specified subnet is used: { "name": "Client_foo", "test": "member('ALL')", - "only-if-required": true + "only-in-additional-list": true }, ... ], @@ -3195,7 +3195,7 @@ specified subnet is used: "pool": "2001:db8:1::-2001:db8:1::ffff" } ], - "require-client-classes": [ "Client_foo" ], + "evaluate-additional-classes": [ "Client_foo" ], ... }, ... @@ -3203,22 +3203,29 @@ specified subnet is used: ... } -Required evaluation can be used to express complex dependencies like +Additional evaluation can be used to express complex dependencies like subnet membership. It can also be used to reverse the precedence; if ``option-data`` is set in a subnet, it takes precedence over ``option-data`` in a class. If ``option-data`` is moved to a required class and required in the subnet, a class evaluated earlier may take precedence. -Required evaluation is also available at shared network and pool/pd-pool -levels. The order in which required classes are considered is: +Additional evaluation is also available at shared network and pool/pd-pool +levels. The order in which additional 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 +Since Kea version 2.7.4 additional classes configured without a test expression are unconditionally added, i.e. they are considered to always be evaluated to ``true``. +.. note:: + + As of Kea version 2.7.4, ``only-if-required`` and ``require-client-classes`` + have been renamed to ``only-in-additional-list`` and ``evaluate-additional-classes`` + respectivley. The original names will still be accepted as input to allow + users to migrate but will eventually be unsupported. + .. _dhcp6-ddns-config: DDNS for DHCPv6 @@ -4656,12 +4663,12 @@ For example: { "name": "dependent-class", "test": "member('KNOWN')", - "only-if-required": true + "only-in-additional-list": true } ] } -The ``only-if-required`` parameter is needed here to force +The ``only-in-additional-list`` parameter is needed here to force evaluation of the class after the lease has been allocated and thus the reserved class has been also assigned. @@ -4669,21 +4676,21 @@ reserved class has been also assigned. The classes specified in non-global host reservations are assigned to the processed packet after all classes with the - ``only-if-required`` parameter set to ``false`` have been evaluated. + ``only-in-additional-list` parameter set to ``false`` have been evaluated. This means that these classes must not depend on the statically assigned classes from the host reservations. If - such a dependency is needed, the ``only-if-required`` must + such a dependency is needed, the ``only-in-addtional-list`` must be set to ``true`` for the dependent classes. Such classes are evaluated after the static classes have been assigned to the packet. This, however, imposes additional configuration overhead, because - all classes marked as ``only-if-required`` must be listed in the - ``require-client-classes`` list for every subnet where they are used. + all classes marked as ``only-in-addtional-list`` must be listed in the + ``evaluate-additional-classes`` list for every subnet where they are used. .. note:: Client classes specified within the Kea configuration file may depend on the classes specified within the global host reservations. - In such a case the ``only-if-required`` parameter is not needed. + In such a case the ``only-in-additional-list`` parameter is not needed. Refer to the :ref:`pool-selection-with-class-reservations6` and :ref:`subnet-selection-with-class-reservations6` for specific use cases. diff --git a/doc/sphinx/arm/hooks-class-cmds.rst b/doc/sphinx/arm/hooks-class-cmds.rst index afaac5a576..071ac93321 100644 --- a/doc/sphinx/arm/hooks-class-cmds.rst +++ b/doc/sphinx/arm/hooks-class-cmds.rst @@ -229,7 +229,7 @@ are returned in the following format: "client-classes": [ { "name": "pxeclient", - "only-if-required": true, + "only-in-additional-list": true, "test": "option[vendor-class-identifier].text == 'PXEClient'", "option-def": [ { @@ -251,5 +251,13 @@ Note that the example above is DHCPv4-specific; the last three parameters are only returned by the DHCPv4 server and are never returned by the DHCPv6 server. Also, some of the parameters provided in this example may not be returned if they are not specified for the class. -Specifically, ``only-if-required``, ``test``, and ``option-def`` are not +Specifically, ``only-in-additional-list``, ``test``, and ``option-def`` are not returned if they are not specified for the class. + +.. note:: + + As of Kea version 2.7.4, ``only-if-required`` has been replaced with + ``only-in-additional-list`` and deprecated. In order to allow users time + to migrate class commands will still accept it as input but translate it + to ``only-in-additional-list`` on output. Eventually support for the + old name wil be removed. diff --git a/doc/sphinx/arm/hooks-limits.rst b/doc/sphinx/arm/hooks-limits.rst index 73c6d2773b..105d4bd6ab 100644 --- a/doc/sphinx/arm/hooks-limits.rst +++ b/doc/sphinx/arm/hooks-limits.rst @@ -142,7 +142,7 @@ checked against the configured limits - are updated for each lease in the follow * ``lease6_renew`` * ``lease6_rebind`` -As a result, classes for which ``"only-if-required"`` is "true" cannot be lease-limited. +As a result, classes for which ``"only-in-additional-list"`` is "true" cannot be lease-limited. Please refer to :ref:`the classification steps ` for more information on which client classes can be used to limit the number of leases. @@ -185,7 +185,7 @@ In terms of rate limiting, client classes are evaluated at the ``pkt4_receive`` ``pkt6_receive`` callout, respectively, so that rate limits are checked as early as possible in the packet-processing cycle. Thus, only those classes which are assigned to the packet solely via an independent test expression can be used. Classes that depend on host reservations or the special -``BOOTP`` or ``KNOWN`` classes, and classes that are marked with ``"only-if-required": true``, +``BOOTP`` or ``KNOWN`` classes, and classes that are marked with ``"only-in-additional-list": true``, cannot be rate limited. See :ref:`the classification steps ` for more details on which client classes can be used to limit the packet rate. diff --git a/src/bin/dhcp4/dhcp4_lexer.cc b/src/bin/dhcp4/dhcp4_lexer.cc index 61cacbb1b2..0ed9e3a7c5 100644 --- a/src/bin/dhcp4/dhcp4_lexer.cc +++ b/src/bin/dhcp4/dhcp4_lexer.cc @@ -4326,7 +4326,7 @@ YY_RULE_SETUP case isc::dhcp::Parser4Context::CLIENT_CLASSES: return isc::dhcp::Dhcp4Parser::make_ONLY_IN_ADDITIONAL_LIST(driver.loc_); default: - return isc::dhcp::Dhcp4Parser::make_STRING("in-additional-list", driver.loc_); + return isc::dhcp::Dhcp4Parser::make_STRING("only-in-additional-list", driver.loc_); } } YY_BREAK diff --git a/src/bin/dhcp4/dhcp4_lexer.ll b/src/bin/dhcp4/dhcp4_lexer.ll index 13ce154421..12f2eb3524 100644 --- a/src/bin/dhcp4/dhcp4_lexer.ll +++ b/src/bin/dhcp4/dhcp4_lexer.ll @@ -1383,7 +1383,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu] case isc::dhcp::Parser4Context::CLIENT_CLASSES: return isc::dhcp::Dhcp4Parser::make_ONLY_IN_ADDITIONAL_LIST(driver.loc_); default: - return isc::dhcp::Dhcp4Parser::make_STRING("in-additional-list", driver.loc_); + return isc::dhcp::Dhcp4Parser::make_STRING("only-in-additional-list", driver.loc_); } } diff --git a/src/bin/dhcp4/dhcp4_messages.cc b/src/bin/dhcp4/dhcp4_messages.cc index fdfec23493..e9a0ef32e5 100644 --- a/src/bin/dhcp4/dhcp4_messages.cc +++ b/src/bin/dhcp4/dhcp4_messages.cc @@ -7,6 +7,10 @@ namespace isc { namespace dhcp { +extern const isc::log::MessageID DHCP4_ADDITIONAL_CLASS_EVAL_ERROR = "DHCP4_ADDITIONAL_CLASS_EVAL_ERROR"; +extern const isc::log::MessageID DHCP4_ADDITIONAL_CLASS_EVAL_RESULT = "DHCP4_ADDITIONAL_CLASS_EVAL_RESULT"; +extern const isc::log::MessageID DHCP4_ADDITIONAL_CLASS_NO_TEST = "DHCP4_ADDITIONAL_CLASS_NO_TEST"; +extern const isc::log::MessageID DHCP4_ADDITIONAL_CLASS_UNDEFINED = "DHCP4_ADDITIONAL_CLASS_UNDEFINED"; extern const isc::log::MessageID DHCP4_ALREADY_RUNNING = "DHCP4_ALREADY_RUNNING"; extern const isc::log::MessageID DHCP4_BUFFER_RECEIVED = "DHCP4_BUFFER_RECEIVED"; extern const isc::log::MessageID DHCP4_BUFFER_RECEIVE_FAIL = "DHCP4_BUFFER_RECEIVE_FAIL"; @@ -146,10 +150,6 @@ extern const isc::log::MessageID DHCP4_RELEASE_FAIL = "DHCP4_RELEASE_FAIL"; extern const isc::log::MessageID DHCP4_RELEASE_FAIL_NO_LEASE = "DHCP4_RELEASE_FAIL_NO_LEASE"; extern const isc::log::MessageID DHCP4_RELEASE_FAIL_WRONG_CLIENT = "DHCP4_RELEASE_FAIL_WRONG_CLIENT"; 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_NO_TEST = "DHCP4_REQUIRED_CLASS_NO_TEST"; -extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNDEFINED = "DHCP4_REQUIRED_CLASS_UNDEFINED"; 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"; @@ -185,6 +185,10 @@ extern const isc::log::MessageID DHCP4_V6_ONLY_PREFERRED_MISSING_IN_OFFER = "DHC namespace { const char* values[] = { + "DHCP4_ADDITIONAL_CLASS_EVAL_ERROR", "%1: Expression '%2' evaluated to %3", + "DHCP4_ADDITIONAL_CLASS_EVAL_RESULT", "%1: Expression '%2' evaluated to %3", + "DHCP4_ADDITIONAL_CLASS_NO_TEST", "additional class %1 has no test expression, adding it to client's classes unconditionally", + "DHCP4_ADDITIONAL_CLASS_UNDEFINED", "additional class %1 has no definition", "DHCP4_ALREADY_RUNNING", "%1 already running? %2", "DHCP4_BUFFER_RECEIVED", "received buffer from %1:%2 to %3:%4 over interface %5", "DHCP4_BUFFER_RECEIVE_FAIL", "error on attempt to receive packet: %1", @@ -324,10 +328,6 @@ const char* values[] = { "DHCP4_RELEASE_FAIL_NO_LEASE", "%1: client is trying to release non-existing lease %2", "DHCP4_RELEASE_FAIL_WRONG_CLIENT", "%1: client is trying to release the lease %2 which belongs to a different client", "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_NO_TEST", "required class %1 has no test expression, adding it to client's classes unconditionally", - "DHCP4_REQUIRED_CLASS_UNDEFINED", "required class %1 has no definition", "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", diff --git a/src/bin/dhcp4/dhcp4_messages.h b/src/bin/dhcp4/dhcp4_messages.h index 56795aec23..01c8c91308 100644 --- a/src/bin/dhcp4/dhcp4_messages.h +++ b/src/bin/dhcp4/dhcp4_messages.h @@ -8,6 +8,10 @@ namespace isc { namespace dhcp { +extern const isc::log::MessageID DHCP4_ADDITIONAL_CLASS_EVAL_ERROR; +extern const isc::log::MessageID DHCP4_ADDITIONAL_CLASS_EVAL_RESULT; +extern const isc::log::MessageID DHCP4_ADDITIONAL_CLASS_NO_TEST; +extern const isc::log::MessageID DHCP4_ADDITIONAL_CLASS_UNDEFINED; extern const isc::log::MessageID DHCP4_ALREADY_RUNNING; extern const isc::log::MessageID DHCP4_BUFFER_RECEIVED; extern const isc::log::MessageID DHCP4_BUFFER_RECEIVE_FAIL; @@ -147,10 +151,6 @@ extern const isc::log::MessageID DHCP4_RELEASE_FAIL; extern const isc::log::MessageID DHCP4_RELEASE_FAIL_NO_LEASE; 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_NO_TEST; -extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNDEFINED; 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; diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index 98e285b7d1..86be2e668d 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -968,26 +968,26 @@ about the address that the client would like to have allocated. If there is no hint, the argument should provide the text indicating that the hint hasn't been sent. -% DHCP4_REQUIRED_CLASS_EVAL_ERROR %1: Expression '%2' evaluated to %3 +% DHCP4_ADDITIONAL_CLASS_EVAL_ERROR %1: Expression '%2' evaluated to %3 This error message indicates that there a problem was encountered while -evaluating an expression of a required client class that was marked as required. +evaluating an expression of a additional client class that was marked as additional. A description of the problem is printed. -% DHCP4_REQUIRED_CLASS_EVAL_RESULT %1: Expression '%2' evaluated to %3 +% DHCP4_ADDITIONAL_CLASS_EVAL_RESULT %1: Expression '%2' evaluated to %3 Logged at debug log level 50. -This debug message indicates that the expression of a required client class has +This debug message indicates that the expression of a additional client class has been successfully evaluated. The client class name and the result value of the evaluation are printed. -% DHCP4_REQUIRED_CLASS_NO_TEST required class %1 has no test expression, adding it to client's classes unconditionally +% DHCP4_ADDITIONAL_CLASS_NO_TEST additional class %1 has no test expression, adding it to client's classes unconditionally Logged at debug log level 40. -This debug message informs that a class was listed for required evaluation but +This debug message informs that a class was listed for additional evaluation but its definition does not include a test expression to evaluate. The class is unconditionally added to the query. -% DHCP4_REQUIRED_CLASS_UNDEFINED required class %1 has no definition +% DHCP4_ADDITIONAL_CLASS_UNDEFINED additional class %1 has no definition Logged at debug log level 40. -This debug message informs that a class is listed for required evaluation but +This debug message informs that a class is listed for additional evaluation but has no definition. The class is ignored. % DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED Multi-threading is enabled and host reservations lookup is always performed first. diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 5e11dfd39d..a26de8b15f 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -2523,7 +2523,6 @@ Dhcpv4Srv::appendBasicOptions(Dhcpv4Exchange& ex) { // Check whether option has been configured. for (auto const& copts : co_list) { OptionDescriptor desc = copts->get(DHCP4_OPTION_SPACE, required); - /// @todo TKM - not sure if otion class-tagging should be allowed here? if (desc.option_ && desc.allowedForClientClasses(cclasses)) { resp->addOption(desc.option_); break; @@ -4939,7 +4938,7 @@ void Dhcpv4Srv::evaluateAdditionalClasses(Dhcpv4Exchange& ex) { const ClientClassDefPtr class_def = dict->findClass(cclass); if (!class_def) { LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, - DHCP4_REQUIRED_CLASS_UNDEFINED) + DHCP4_ADDITIONAL_CLASS_UNDEFINED) .arg(cclass); // Ignore it as it can't have an attached action continue; @@ -4948,7 +4947,7 @@ void Dhcpv4Srv::evaluateAdditionalClasses(Dhcpv4Exchange& ex) { // Add a class without an expression to evaluate if (!expr_ptr) { LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, - DHCP4_REQUIRED_CLASS_NO_TEST) + DHCP4_ADDITIONAL_CLASS_NO_TEST) .arg(cclass); query->addClass(cclass); continue; @@ -4957,7 +4956,7 @@ void Dhcpv4Srv::evaluateAdditionalClasses(Dhcpv4Exchange& ex) { // true (match) or raise an exception (error) try { bool status = evaluateBool(*expr_ptr, *query); - LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_REQUIRED_CLASS_EVAL_RESULT) + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_ADDITIONAL_CLASS_EVAL_RESULT) .arg(query->getLabel()) .arg(cclass) .arg(status ? "true" : "false"); @@ -4966,7 +4965,7 @@ void Dhcpv4Srv::evaluateAdditionalClasses(Dhcpv4Exchange& ex) { query->addClass(cclass); } } catch (const Exception& ex) { - LOG_ERROR(dhcp4_logger, DHCP4_REQUIRED_CLASS_EVAL_ERROR) + LOG_ERROR(dhcp4_logger, DHCP4_ADDITIONAL_CLASS_EVAL_ERROR) .arg(query->getLabel()) .arg(cclass) .arg(ex.what()); diff --git a/src/bin/dhcp4/tests/classify_unittest.cc b/src/bin/dhcp4/tests/classify_unittest.cc index 34b55d83b6..6fb882baf2 100644 --- a/src/bin/dhcp4/tests/classify_unittest.cc +++ b/src/bin/dhcp4/tests/classify_unittest.cc @@ -246,19 +246,19 @@ const char* CONFIGS[] = { "{" " \"name\": \"pxe1\"," " \"test\": \"option[93].hex == 0x0009\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"next-server\": \"1.2.3.4\"" "}," "{" " \"name\": \"pxe2\"," " \"test\": \"option[93].hex == 0x0007\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"server-hostname\": \"deneb\"" "}," "{" " \"name\": \"pxe3\"," " \"test\": \"option[93].hex == 0x0006\"," - " \"only-if-required\": false," + " \"only-in-additional-list\": false," " \"boot-file-name\": \"pxelinux.0\"" "}," "{" @@ -778,7 +778,7 @@ TEST_F(ClassifyTest, fixedFieldsInformNoClasses3) { testFixedFields(CONFIGS[3], DHCPINFORM, OptionPtr(), "0.0.0.0", "", ""); } -// Class 'pxe1' is only-if-required and not subject to required evaluation +// Class 'pxe1' is only-in-additional-list and not subject to additional evaluation TEST_F(ClassifyTest, fixedFieldsDiscoverNextServer3) { OptionPtr pxe(new OptionInt(Option::V4, 93, 0x0009)); @@ -795,7 +795,7 @@ TEST_F(ClassifyTest, fixedFieldsInformNextServer3) { testFixedFields(CONFIGS[3], DHCPINFORM, pxe, "0.0.0.0", "", ""); } -// Class pxe2 is only-if-required but the subnet requires its evaluation +// Class pxe2 is only-in-additional-list but the subnet requires its evaluation TEST_F(ClassifyTest, fixedFieldsDiscoverHostname3) { OptionPtr pxe(new OptionInt(Option::V4, 93, 0x0007)); @@ -944,7 +944,7 @@ TEST_F(ClassifyTest, server2computer) { EXPECT_EQ("10.0.0.160", resp->getYiaddr().toText()); } -// This test checks the precedence order in required evaluation. +// This test checks the precedence order in additional evaluation. // This order is: pools > subnet > shared-network TEST_F(ClassifyTest, precedenceNone) { std::string config = @@ -957,7 +957,7 @@ TEST_F(ClassifyTest, precedenceNone) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.1\"" @@ -966,7 +966,7 @@ TEST_F(ClassifyTest, precedenceNone) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.2\"" @@ -975,7 +975,7 @@ TEST_F(ClassifyTest, precedenceNone) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.3\"" @@ -1025,7 +1025,7 @@ TEST_F(ClassifyTest, precedencePool) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.1\"" @@ -1034,7 +1034,7 @@ TEST_F(ClassifyTest, precedencePool) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.2\"" @@ -1043,7 +1043,7 @@ TEST_F(ClassifyTest, precedencePool) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.3\"" @@ -1102,7 +1102,7 @@ TEST_F(ClassifyTest, precedenceSubnet) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.1\"" @@ -1111,7 +1111,7 @@ TEST_F(ClassifyTest, precedenceSubnet) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.2\"" @@ -1120,7 +1120,7 @@ TEST_F(ClassifyTest, precedenceSubnet) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.3\"" @@ -1178,7 +1178,7 @@ TEST_F(ClassifyTest, precedenceNetwork) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.1\"" @@ -1187,7 +1187,7 @@ TEST_F(ClassifyTest, precedenceNetwork) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.2\"" @@ -1196,7 +1196,7 @@ TEST_F(ClassifyTest, precedenceNetwork) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"domain-name-servers\"," " \"data\": \"10.0.0.3\"" diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 5af406ca62..161b175fe2 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -8121,11 +8121,18 @@ TEST_F(Dhcp4ParserTest, deprecatedRequireClientClassesCheck) { R"^( "rebind-timer": 2000, "renew-timer": 1000, - "subnet4": [{ - "require-client-classes": [ "foo" ], - "pools": [{ "pool": "192.0.2.0/28" }], - "id": 1, - "subnet": "192.0.2.0/24" + "shared-networks":[{ + "name": "net1", + "require-client-classes": [ "one" ], + "subnet4": [{ + "require-client-classes": [ "two" ], + "pools": [{ + "pool": "192.0.2.0/28", + "require-client-classes": [ "three" ] + }], + "id": 1, + "subnet": "192.0.2.0/24" + }], }], "valid-lifetime": 400 })^"; @@ -8138,14 +8145,31 @@ TEST_F(Dhcp4ParserTest, deprecatedRequireClientClassesCheck) { ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json)); checkResult(status, 0); + SharedNetwork4Ptr network = CfgMgr::instance().getStagingCfg()-> + getCfgSharedNetworks4()->getByName("net1"); + ASSERT_TRUE(network); + + auto& net_class_list = network->getAdditionalClasses(); + EXPECT_EQ(1, net_class_list.size()); + auto cclasses = net_class_list.begin(); + EXPECT_EQ(*cclasses, "one"); + Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.0")); ASSERT_TRUE(subnet); - const auto& cclass_list = subnet->getAdditionalClasses(); - EXPECT_EQ(1, cclass_list.size()); - auto cclasses = cclass_list.begin(); - EXPECT_EQ(*cclasses, "foo"); + auto& sub_class_list = subnet->getAdditionalClasses(); + EXPECT_EQ(1, sub_class_list.size()); + cclasses = sub_class_list.begin(); + EXPECT_EQ(*cclasses, "two"); + + PoolPtr pool = subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.0"), false); + ASSERT_TRUE(pool); + + auto& pool_class_list = pool->getAdditionalClasses(); + EXPECT_EQ(1, pool_class_list.size()); + cclasses = pool_class_list.begin(); + EXPECT_EQ(*cclasses, "three"); // Now verify that users cannot specify both. config = "{ " + genIfaceConfig() + "," diff --git a/src/bin/dhcp6/dhcp6_messages.cc b/src/bin/dhcp6/dhcp6_messages.cc index 452a4aefa8..24cdc04676 100644 --- a/src/bin/dhcp6/dhcp6_messages.cc +++ b/src/bin/dhcp6/dhcp6_messages.cc @@ -8,6 +8,10 @@ namespace isc { namespace dhcp { extern const isc::log::MessageID DHCP4_HOOK_SUBNET6_SELECT_PARKING_LOT_FULL = "DHCP4_HOOK_SUBNET6_SELECT_PARKING_LOT_FULL"; +extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_ERROR = "DHCP6_ADDITIONAL_CLASS_EVAL_ERROR"; +extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_RESULT = "DHCP6_ADDITIONAL_CLASS_EVAL_RESULT"; +extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_NO_TEST = "DHCP6_ADDITIONAL_CLASS_NO_TEST"; +extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_UNDEFINED = "DHCP6_ADDITIONAL_CLASS_UNDEFINED"; extern const isc::log::MessageID DHCP6_ADD_GLOBAL_STATUS_CODE = "DHCP6_ADD_GLOBAL_STATUS_CODE"; extern const isc::log::MessageID DHCP6_ADD_STATUS_CODE_FOR_IA = "DHCP6_ADD_STATUS_CODE_FOR_IA"; extern const isc::log::MessageID DHCP6_ALREADY_RUNNING = "DHCP6_ALREADY_RUNNING"; @@ -145,10 +149,6 @@ extern const isc::log::MessageID DHCP6_RELEASE_PD_EXPIRED = "DHCP6_RELEASE_PD_EX extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL = "DHCP6_RELEASE_PD_FAIL"; extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_DUID = "DHCP6_RELEASE_PD_FAIL_WRONG_DUID"; 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_NO_TEST = "DHCP6_REQUIRED_CLASS_NO_TEST"; -extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNDEFINED = "DHCP6_REQUIRED_CLASS_UNDEFINED"; 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"; @@ -175,6 +175,10 @@ namespace { const char* values[] = { "DHCP4_HOOK_SUBNET6_SELECT_PARKING_LOT_FULL", "The parked-packet-limit %1, has been reached, dropping query: %2", + "DHCP6_ADDITIONAL_CLASS_EVAL_ERROR", "%1: Expression '%2' evaluated to %3", + "DHCP6_ADDITIONAL_CLASS_EVAL_RESULT", "%1: Expression '%2' evaluated to %3", + "DHCP6_ADDITIONAL_CLASS_NO_TEST", "additional class %1 has no test expression, adding it to client's classes unconditionally", + "DHCP6_ADDITIONAL_CLASS_UNDEFINED", "additional class %1 has no definition", "DHCP6_ADD_GLOBAL_STATUS_CODE", "%1: adding Status Code to DHCPv6 packet: %2", "DHCP6_ADD_STATUS_CODE_FOR_IA", "%1: adding Status Code to IA with iaid=%2: %3", "DHCP6_ALREADY_RUNNING", "%1 already running? %2", @@ -312,10 +316,6 @@ const char* values[] = { "DHCP6_RELEASE_PD_FAIL", "%1: failed to release prefix %2/%3 for iaid=%4", "DHCP6_RELEASE_PD_FAIL_WRONG_DUID", "%1: client tried to release prefix %2/%3, but it belongs to another client (duid=%4)", "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_NO_TEST", "required class %1 has no test expression, adding it to client's classes unconditionally", - "DHCP6_REQUIRED_CLASS_UNDEFINED", "required class %1 has no definition", "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", diff --git a/src/bin/dhcp6/dhcp6_messages.h b/src/bin/dhcp6/dhcp6_messages.h index 3afab08b5a..3cb2b2b6cc 100644 --- a/src/bin/dhcp6/dhcp6_messages.h +++ b/src/bin/dhcp6/dhcp6_messages.h @@ -9,6 +9,10 @@ namespace isc { namespace dhcp { extern const isc::log::MessageID DHCP4_HOOK_SUBNET6_SELECT_PARKING_LOT_FULL; +extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_ERROR; +extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_RESULT; +extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_NO_TEST; +extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_UNDEFINED; extern const isc::log::MessageID DHCP6_ADD_GLOBAL_STATUS_CODE; extern const isc::log::MessageID DHCP6_ADD_STATUS_CODE_FOR_IA; extern const isc::log::MessageID DHCP6_ALREADY_RUNNING; @@ -146,10 +150,6 @@ extern const isc::log::MessageID DHCP6_RELEASE_PD_EXPIRED; extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL; 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_NO_TEST; -extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNDEFINED; 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; diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index 50cf131024..0dedfbc3b0 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -973,26 +973,26 @@ client and transaction identification information. The second and third argument identify the prefix. The fourth and fifth argument hold the expected IAID and IAID found respectively. -% DHCP6_REQUIRED_CLASS_EVAL_ERROR %1: Expression '%2' evaluated to %3 -This error message indicates that there a problem was encountered while -evaluating an expression of a required client class that was marked as required. +% DHCP6_ADDITIONAL_CLASS_EVAL_ERROR %1: Expression '%2' evaluated to %3 +This error message indicates that a problem was encountered while +evaluating the expression of an additional client class. A description of the problem is printed. -% DHCP6_REQUIRED_CLASS_EVAL_RESULT %1: Expression '%2' evaluated to %3 +% DHCP6_ADDITIONAL_CLASS_EVAL_RESULT %1: Expression '%2' evaluated to %3 Logged at debug log level 50. -This debug message indicates that the expression of a required client class has +This debug message indicates that the expression of an additional client class has been successfully evaluated. The client class name and the result value of the evaluation are printed. -% DHCP6_REQUIRED_CLASS_NO_TEST required class %1 has no test expression, adding it to client's classes unconditionally +% DHCP6_ADDITIONAL_CLASS_NO_TEST additional class %1 has no test expression, adding it to client's classes unconditionally Logged at debug log level 40. -This debug message informs that a class was listed for required evaluation but +This debug message informs that a class was listed for additional evaluation but its definition does not include a test expression to evaluate. The class is unconditionally added to the query. -% DHCP6_REQUIRED_CLASS_UNDEFINED required class %1 has no definition +% DHCP6_ADDITIONAL_CLASS_UNDEFINED additional class %1 has no definition Logged at debug log level 40. -This debug message informs that a class is listed for required evaluation but +This debug message informs that a class is listed for additional evaluation but has no definition. The class is ignored. % DHCP6_REQUIRED_OPTIONS_CHECK_FAIL %1: %2 message received from %3 failed the following check: %4 diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 84e8d61f03..a926897f88 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -4575,7 +4575,7 @@ Dhcpv6Srv::evaluateAdditionalClasses(const Pkt6Ptr& pkt, AllocEngine::ClientCont const ClientClassDefPtr class_def = dict->findClass(cclass); if (!class_def) { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, - DHCP6_REQUIRED_CLASS_UNDEFINED) + DHCP6_ADDITIONAL_CLASS_UNDEFINED) .arg(cclass); // Ignore it as it can't have an attached action continue; @@ -4584,7 +4584,7 @@ Dhcpv6Srv::evaluateAdditionalClasses(const Pkt6Ptr& pkt, AllocEngine::ClientCont // Add a class without an expression to evaluate if (!expr_ptr) { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, - DHCP6_REQUIRED_CLASS_NO_TEST) + DHCP6_ADDITIONAL_CLASS_NO_TEST) .arg(cclass); pkt->addClass(cclass); continue; @@ -4593,7 +4593,7 @@ Dhcpv6Srv::evaluateAdditionalClasses(const Pkt6Ptr& pkt, AllocEngine::ClientCont // true (match) or raise an exception (error) try { bool status = evaluateBool(*expr_ptr, *pkt); - LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_REQUIRED_CLASS_EVAL_RESULT) + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_ADDITIONAL_CLASS_EVAL_RESULT) .arg(pkt->getLabel()) .arg(cclass) .arg(status ? "true" : "false"); @@ -4602,7 +4602,7 @@ Dhcpv6Srv::evaluateAdditionalClasses(const Pkt6Ptr& pkt, AllocEngine::ClientCont pkt->addClass(cclass); } } catch (const Exception& ex) { - LOG_ERROR(dhcp6_logger, DHCP6_REQUIRED_CLASS_EVAL_ERROR) + LOG_ERROR(dhcp6_logger, DHCP6_ADDITIONAL_CLASS_EVAL_ERROR) .arg(pkt->getLabel()) .arg(cclass) .arg(ex.what()); diff --git a/src/bin/dhcp6/tests/classify_unittest.cc b/src/bin/dhcp6/tests/classify_unittest.cc index 9963193774..dc1a8b500f 100644 --- a/src/bin/dhcp6/tests/classify_unittest.cc +++ b/src/bin/dhcp6/tests/classify_unittest.cc @@ -706,8 +706,8 @@ TEST_F(ClassifyTest, matchClassification) { EXPECT_FALSE(opt3); } -// Check that only-if-required classes are not evaluated by classifyPacket -TEST_F(ClassifyTest, required) { +// Check that only-in-additional-list classes are not evaluated by classifyPacket +TEST_F(ClassifyTest, additional) { IfaceMgrTestConfig test_config(true); NakedDhcpv6Srv srv(0); @@ -734,7 +734,7 @@ TEST_F(ClassifyTest, required) { " \"interface\": \"eth1\" } ]," "\"client-classes\": [ " "{ \"name\": \"router\", " - " \"only-if-required\": true, " + " \"only-in-additional-list\": true, " " \"option-data\": [" " { \"name\": \"ipv6-forwarding\", " " \"data\": \"true\" } ], " @@ -820,8 +820,8 @@ TEST_F(ClassifyTest, required) { EXPECT_FALSE(opt3); } -// Checks that when only-if-required classes are still evaluated -TEST_F(ClassifyTest, requiredClassification) { +// Checks that when only-in-additional-list classes are still evaluated +TEST_F(ClassifyTest, additionalClassification) { IfaceMgrTestConfig test_config(true); NakedDhcpv6Srv srv(0); @@ -845,11 +845,11 @@ TEST_F(ClassifyTest, requiredClassification) { "{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], " " \"id\": 1, " " \"subnet\": \"2001:db8:1::/48\", " - " \"require-client-classes\": [ \"router\" ], " + " \"evaluate-additional-classes\": [ \"router\" ], " " \"interface\": \"eth1\" } ]," "\"client-classes\": [ " "{ \"name\": \"router\", " - " \"only-if-required\": true, " + " \"only-in-additional-list\": true, " " \"option-data\": [" " { \"name\": \"ipv6-forwarding\", " " \"data\": \"true\" } ], " @@ -1890,7 +1890,7 @@ TEST_F(ClassifyTest, member) { EXPECT_FALSE(ipf3->readBoolean()); } -// This test checks the precedence order in required evaluation. +// This test checks the precedence order in additional evaluation. // This order is: pools > subnet > shared-network TEST_F(ClassifyTest, precedenceNone) { std::string config = @@ -1905,7 +1905,7 @@ TEST_F(ClassifyTest, precedenceNone) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::1\"" @@ -1914,7 +1914,7 @@ TEST_F(ClassifyTest, precedenceNone) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::2\"" @@ -1923,7 +1923,7 @@ TEST_F(ClassifyTest, precedenceNone) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::3\"" @@ -1964,7 +1964,7 @@ TEST_F(ClassifyTest, precedenceNone) { EXPECT_FALSE(opt); } -// This test checks the precedence order in required evaluation. +// This test checks the precedence order in additional evaluation. // This order is: pools > subnet > shared-network TEST_F(ClassifyTest, precedencePool) { std::string config = @@ -1976,7 +1976,7 @@ TEST_F(ClassifyTest, precedencePool) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::1\"" @@ -1985,7 +1985,7 @@ TEST_F(ClassifyTest, precedencePool) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::2\"" @@ -1994,7 +1994,7 @@ TEST_F(ClassifyTest, precedencePool) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::3\"" @@ -2004,14 +2004,14 @@ TEST_F(ClassifyTest, precedencePool) { "\"shared-networks\": [ {" " \"name\": \"frog\"," " \"interface\": \"eth1\"," - " \"require-client-classes\": [ \"for-network\" ]," + " \"evaluate-additional-classes\": [ \"for-network\" ]," " \"subnet6\": [ { " " \"subnet\": \"2001:db8:1::/64\"," " \"id\": 1," - " \"require-client-classes\": [ \"for-subnet\" ]," + " \"evaluate-additional-classes\": [ \"for-subnet\" ]," " \"pools\": [ { " " \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\"," - " \"require-client-classes\": [ \"for-pool\" ]" + " \"evaluate-additional-classes\": [ \"for-pool\" ]" " } ]" " } ]" "} ]," @@ -2044,7 +2044,7 @@ TEST_F(ClassifyTest, precedencePool) { EXPECT_EQ("2001:db8:1::1", addrs[0].toText()); } -// This test checks the precedence order in required evaluation. +// This test checks the precedence order in additional evaluation. // This order is: pools > subnet > shared-network TEST_F(ClassifyTest, precedencePdPool) { std::string config = @@ -2056,7 +2056,7 @@ TEST_F(ClassifyTest, precedencePdPool) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::1\"" @@ -2065,7 +2065,7 @@ TEST_F(ClassifyTest, precedencePdPool) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::2\"" @@ -2074,7 +2074,7 @@ TEST_F(ClassifyTest, precedencePdPool) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::3\"" @@ -2084,15 +2084,15 @@ TEST_F(ClassifyTest, precedencePdPool) { "\"shared-networks\": [ {" " \"name\": \"frog\"," " \"interface\": \"eth1\"," - " \"require-client-classes\": [ \"for-network\" ]," + " \"evaluate-additional-classes\": [ \"for-network\" ]," " \"subnet6\": [ { " " \"subnet\": \"2001:db8:1::/64\"," " \"id\": 1," - " \"require-client-classes\": [ \"for-subnet\" ]," + " \"evaluate-additional-classes\": [ \"for-subnet\" ]," " \"pd-pools\": [ { " " \"prefix\": \"2001:db8:1::\"," " \"prefix-len\": 48, \"delegated-len\": 64," - " \"require-client-classes\": [ \"for-pool\" ]" + " \"evaluate-additional-classes\": [ \"for-pool\" ]" " } ]" " } ]" "} ]," @@ -2125,7 +2125,7 @@ TEST_F(ClassifyTest, precedencePdPool) { EXPECT_EQ("2001:db8:1::1", addrs[0].toText()); } -// This test checks the precedence order in required evaluation. +// This test checks the precedence order in additional evaluation. // This order is: pools > subnet > shared-network TEST_F(ClassifyTest, precedenceSubnet) { std::string config = @@ -2137,7 +2137,7 @@ TEST_F(ClassifyTest, precedenceSubnet) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::1\"" @@ -2146,7 +2146,7 @@ TEST_F(ClassifyTest, precedenceSubnet) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::2\"" @@ -2155,7 +2155,7 @@ TEST_F(ClassifyTest, precedenceSubnet) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::3\"" @@ -2165,11 +2165,11 @@ TEST_F(ClassifyTest, precedenceSubnet) { "\"shared-networks\": [ {" " \"name\": \"frog\"," " \"interface\": \"eth1\"," - " \"require-client-classes\": [ \"for-network\" ]," + " \"evaluate-additional-classes\": [ \"for-network\" ]," " \"subnet6\": [ { " " \"subnet\": \"2001:db8:1::/64\"," " \"id\": 1," - " \"require-client-classes\": [ \"for-subnet\" ]," + " \"evaluate-additional-classes\": [ \"for-subnet\" ]," " \"pools\": [ { " " \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\"" " } ]" @@ -2204,7 +2204,7 @@ TEST_F(ClassifyTest, precedenceSubnet) { EXPECT_EQ("2001:db8:1::2", addrs[0].toText()); } -// This test checks the precedence order in required evaluation. +// This test checks the precedence order in additional evaluation. // This order is: pools > subnet > shared-network TEST_F(ClassifyTest, precedenceNetwork) { std::string config = @@ -2216,7 +2216,7 @@ TEST_F(ClassifyTest, precedenceNetwork) { " {" " \"name\": \"for-pool\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::1\"" @@ -2225,7 +2225,7 @@ TEST_F(ClassifyTest, precedenceNetwork) { " {" " \"name\": \"for-subnet\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::2\"" @@ -2234,7 +2234,7 @@ TEST_F(ClassifyTest, precedenceNetwork) { " {" " \"name\": \"for-network\"," " \"test\": \"member('ALL')\"," - " \"only-if-required\": true," + " \"only-in-additional-list\": true," " \"option-data\": [ {" " \"name\": \"dns-servers\"," " \"data\": \"2001:db8:1::3\"" @@ -2244,7 +2244,7 @@ TEST_F(ClassifyTest, precedenceNetwork) { "\"shared-networks\": [ {" " \"name\": \"frog\"," " \"interface\": \"eth1\"," - " \"require-client-classes\": [ \"for-network\" ]," + " \"evaluate-additional-classes\": [ \"for-network\" ]," " \"subnet6\": [ { " " \"subnet\": \"2001:db8:1::/64\"," " \"id\": 1," @@ -2282,9 +2282,9 @@ 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 +// This test checks a additional class without a test entry can be // unconditionally added. -TEST_F(ClassifyTest, requiredNoTest) { +TEST_F(ClassifyTest, additionalNoTest) { std::string config = "{" "\"interfaces-config\": {" @@ -2302,7 +2302,7 @@ TEST_F(ClassifyTest, requiredNoTest) { "\"shared-networks\": [ {" " \"name\": \"frog\"," " \"interface\": \"eth1\"," - " \"require-client-classes\": [ \"for-network\" ]," + " \"evaluate-additional-classes\": [ \"for-network\" ]," " \"subnet6\": [ { " " \"subnet\": \"2001:db8:1::/64\"," " \"id\": 1," @@ -2340,10 +2340,10 @@ TEST_F(ClassifyTest, requiredNoTest) { EXPECT_EQ("2001:db8:1::3", addrs[0].toText()); } -// This test checks a required class which is not defined is ignored. +// This test checks a additional class which is not defined is ignored. // Please set KEA_LOGGER_DESTINATION to stderr or stdout and check // that DHCP6_REQUIRED_CLASS_UNDEFINED is logged, -TEST_F(ClassifyTest, requiredNotDefined) { +TEST_F(ClassifyTest, additionalNotDefined) { std::string config = "{" "\"interfaces-config\": {" @@ -2352,7 +2352,7 @@ TEST_F(ClassifyTest, requiredNotDefined) { "\"shared-networks\": [ {" " \"name\": \"frog\"," " \"interface\": \"eth1\"," - " \"require-client-classes\": [ \"for-network\" ]," + " \"evaluate-additional-classes\": [ \"for-network\" ]," " \"subnet6\": [ { " " \"subnet\": \"2001:db8:1::/64\"," " \"id\": 1," diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 0d542609b3..3bfceee751 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -9105,11 +9105,24 @@ TEST_F(Dhcp6ParserTest, deprecatedRequireClientClassesCheck) { R"^( "rebind-timer": 2000, "renew-timer": 1000, - "subnet6": [{ - "require-client-classes": [ "foo" ], - "pools": [{ "pool": "2001:db8::/64" }], - "id": 1, - "subnet": "2001:db8::/64" + "shared-networks":[{ + "name": "net1", + "require-client-classes": [ "one" ], + "subnet6": [{ + "require-client-classes": [ "two" ], + "pools": [{ + "pool": "2001:db8::/64", + "require-client-classes": [ "three" ], + }], + "pd-pools": [{ + "prefix": "3001:db8::", + "prefix-len": 56, + "delegated-len": 64, + "require-client-classes": [ "four" ], + }], + "id": 1, + "subnet": "2001:db8::/64" + }], }], "valid-lifetime": 400 })^"; @@ -9122,14 +9135,39 @@ TEST_F(Dhcp6ParserTest, deprecatedRequireClientClassesCheck) { ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json)); checkResult(status, 0); + SharedNetwork6Ptr network = CfgMgr::instance().getStagingCfg()-> + getCfgSharedNetworks6()->getByName("net1"); + ASSERT_TRUE(network); + + auto& net_class_list = network->getAdditionalClasses(); + EXPECT_EQ(1, net_class_list.size()); + auto cclasses = net_class_list.begin(); + EXPECT_EQ(*cclasses, "one"); + Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()-> getCfgSubnets6()->selectSubnet(IOAddress("2001:db8::")); ASSERT_TRUE(subnet); - const auto& cclass_list = subnet->getAdditionalClasses(); - EXPECT_EQ(1, cclass_list.size()); - auto cclasses = cclass_list.begin(); - EXPECT_EQ(*cclasses, "foo"); + const auto& sub_class_list = subnet->getAdditionalClasses(); + EXPECT_EQ(1, sub_class_list.size()); + cclasses = sub_class_list.begin(); + EXPECT_EQ(*cclasses, "two"); + + PoolPtr pool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8::"), false); + ASSERT_TRUE(pool); + + auto& pool_class_list = pool->getAdditionalClasses(); + EXPECT_EQ(1, pool_class_list.size()); + cclasses = pool_class_list.begin(); + EXPECT_EQ(*cclasses, "three"); + + pool = subnet->getPool(Lease::TYPE_PD, IOAddress("3001:db8::"), false); + ASSERT_TRUE(pool); + + auto& pd_pool_class_list = pool->getAdditionalClasses(); + EXPECT_EQ(1, pd_pool_class_list.size()); + cclasses = pd_pool_class_list.begin(); + EXPECT_EQ(*cclasses, "four"); // Now verify that users cannot specify both. config = "{ " + genIfaceConfig() + "," 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 c0fb848903..af764e8e98 100644 --- a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc @@ -2149,7 +2149,7 @@ TEST_F(ClientClassDefParserTest, offerLftInvalid) { } TEST_F(ClientClassDefParserTest, deprecatedOnlyIfRequired) { - // Valid entry. + // Valid entry using only-if-required. std::string cfg_text = R"^({ "name": "foo", @@ -2164,6 +2164,20 @@ TEST_F(ClientClassDefParserTest, deprecatedOnlyIfRequired) { EXPECT_EQ("foo", cclass->getName()); ASSERT_TRUE(cclass->getAdditional()); + // Valid entry using only-in-additional-list. + cfg_text = + R"^({ + "name": "foo", + "only-in-additional-list": true + })^"; + + ASSERT_NO_THROW(cclass = parseClientClassDef(cfg_text, AF_INET)); + + // Class should exist. + ASSERT_TRUE(cclass); + EXPECT_EQ("foo", cclass->getName()); + ASSERT_TRUE(cclass->getAdditional()); + // Invalid entry specifies both parameters. std::string cfg_text2 = R"^({ diff --git a/src/share/api/class-get.json b/src/share/api/class-get.json index 1d7c5b2906..b2c0bb15d2 100644 --- a/src/share/api/class-get.json +++ b/src/share/api/class-get.json @@ -26,7 +26,7 @@ " \"client-classes\": [", " {", " \"name\": ,", - " \"only-if-required\": ,", + " \"only-in-additional-list\": ,", " \"test\": ,", " \"option-data\": [