From: Francis Dupont Date: Fri, 6 Apr 2018 14:22:09 +0000 (+0200) Subject: [5374] Addressed non test comments - checkpoint before regen X-Git-Tag: trac5458a_base~14^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=339f45b6b915ed995c471713fbcf536395eb34c9;p=thirdparty%2Fkea.git [5374] Addressed non test comments - checkpoint before regen --- diff --git a/doc/Makefile.am b/doc/Makefile.am index 2be574a095..c39619a795 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -18,6 +18,7 @@ nobase_dist_doc_DATA += examples/kea4/advanced.json nobase_dist_doc_DATA += examples/kea4/backends.json nobase_dist_doc_DATA += examples/kea4/cassandra.json nobase_dist_doc_DATA += examples/kea4/classify.json +nobase_dist_doc_DATA += examples/kea4/classify2.json nobase_dist_doc_DATA += examples/kea4/dhcpv4-over-dhcpv6.json nobase_dist_doc_DATA += examples/kea4/hooks.json nobase_dist_doc_DATA += examples/kea4/leases-expiration.json @@ -33,6 +34,7 @@ nobase_dist_doc_DATA += examples/kea6/advanced.json nobase_dist_doc_DATA += examples/kea6/backends.json nobase_dist_doc_DATA += examples/kea6/cassandra.json nobase_dist_doc_DATA += examples/kea6/classify.json +nobase_dist_doc_DATA += examples/kea6/classify2.json nobase_dist_doc_DATA += examples/kea6/dhcpv4-over-dhcpv6.json nobase_dist_doc_DATA += examples/kea6/duid.json nobase_dist_doc_DATA += examples/kea6/hooks.json diff --git a/doc/examples/kea4/classify2.json b/doc/examples/kea4/classify2.json new file mode 100644 index 0000000000..626d5c9ffb --- /dev/null +++ b/doc/examples/kea4/classify2.json @@ -0,0 +1,149 @@ +// This is an example configuration file for the DHCPv4 server in Kea. +// The purpose of this example is to showcase how clients can be classified +// with advanced features. + +{ "Dhcp4": { + +// Kea is told to listen on ethX interface only. + "interfaces-config": { + "interfaces": [ "ethX" ] + }, + +// Let's use the simplest backend: memfile and use some reasonable values +// for timers. They are of no concern for the classification demonstration. + "lease-database": { "type": "memfile" }, + "renew-timer": 1000, + "rebind-timer": 2000, + "valid-lifetime": 4000, + +// This list defines several classes that incoming packets can be assigned to. +// One packet can belong to zero or more classes. + "client-classes": [ + +// This class is required by the second subnet and is evaluated only +// if it is required. The test expression returns true. +// Note it is not possible to depend on VoIP class because it is not yet +// defined. + { + "name": "second_subnet", + "only-if-required": true, + "test": "member('ALL')", + "option-data": [{ + "name": "domain-name-servers", + "data": "127.0.0.1" + }] + }, + +// Let's classify all incoming DISCOVER (message type 1) to a separate +// class. + { + "name": "discovers", + "test": "pkt4.msgtype == 1" + }, + +// Clients are supposed to set the transaction-id field to a random value. +// Clients that send it with 0 are most likely broken. Let's mark them +// as such. + { + "name": "broken", + "test": "pkt4.transid == 0" + }, + +// Let's pick VoIP phones. Those that send their class identifiers +// as Aastra, should belong to VoIP class. For a list of all options, +// see www.iana.org/assignments/bootp-dhcp-parameters/. +// In this particular class, we want to set specific values +// of certain DHCPv4 fields. If the incoming packet matches the +// test, those fields will be set in outgoing responses. +// The option 43 is defined to encapsulate suboption in the aastra space. + { + "name": "VoIP", + "test": "substring(option[60].hex,0,6) == 'Aastra'", + "next-server": "192.0.2.254", + "server-hostname": "hal9000", + "boot-file-name": "/dev/null", + "option-def": [ { + "name": "vendor-encapsulated-options", + "code": 43, + "type": "empty", + "encapsulate": "aastra" } ] + }, + +// Both a VoIP phone (by evaluation or host reservation) and has a host +// reservation. + { + "name": "VoIP_host", + "test": "member('VoIP') and member('KNOWN')", + "server-hostname": "hal9001" + } + + ], + +// The following list defines subnets. For some subnets we defined +// a class that is allowed in that subnet. If not specified, +// everyone is allowed. When a class is specified, only packets belonging +// to that class are allowed for that subnet. + "subnet4": [ + { +// This one is for VoIP devices only. + "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ], + "subnet": "192.0.2.0/24", + "client-class": "VoIP", + "interface": "ethX" + }, +// This one doesn't have any client-class specified, so everyone +// is allowed in. The normal subnet selection rules still apply, +// though. There is also a static class reservation for a client +// using MAC address 1a:1b:1c:1d:1e:1f. This client will always +// be assigned to this class. + { + "pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ], + "subnet": "192.0.3.0/24", + "reservations": [ + { + "hw-address": "1a:1b:1c:1d:1e:1f", + "client-classes": [ "VoIP" ] + } ], + "interface": "ethX" + }, + +// The following list defines a subnet with pools. For some pools +// we defined a class that is allowed in that pool. If not specified +// everyone is allowed. When a class is specified, only packets belonging +// to that class are allowed for that pool. + { + "pools": [ + { +// This one is for VoIP devices only. + "pool": "192.0.4.1 - 192.0.4.200", + "client-class": "VoIP" + }, +// This one doesn't have any client-class specified, so everyone +// is allowed in. + { + "pool": "192.0.5.1 - 192.0.5.200" + } ], + "subnet": "192.0.4.0/23", + "interface": "ethY" + } + ] +}, + +// The following configures logging. It assumes that messages with at +// least informational level (info, warn, error and fatal) should be +// logged to stdout. +"Logging": { + "loggers": [ + { + "name": "kea-dhcp4", + "output_options": [ + { + "output": "stdout" + } + ], + "severity": "INFO" + } + ] +} + +} diff --git a/doc/examples/kea6/classify2.json b/doc/examples/kea6/classify2.json new file mode 100644 index 0000000000..aa6a7bd4df --- /dev/null +++ b/doc/examples/kea6/classify2.json @@ -0,0 +1,121 @@ +// This is an example configuration file for the DHCPv4 server in Kea. +// The purpose of this example is to showcase how clients can be classified. + +{ "Dhcp6": + +{ +// Kea is told to listen on ethX interface only. + "interfaces-config": { + "interfaces": [ "ethX" ] + }, + +// Let's use the simplest backend: memfile and use some reasonable values +// for timers. They are of no concern for the classification demonstration. + "lease-database": { + "type": "memfile", + "lfc-interval": 3600 + }, + "renew-timer": 1000, + "rebind-timer": 2000, + "preferred-lifetime": 3000, + "valid-lifetime": 4000, + +// This list defines several classes that incoming packets can be assigned to. +// One packet can belong to zero or more classes. + "client-classes": [ + +// This class is required by the second subnet and is evaluated only +// if it is required. The test expression returns true. +// Note it is not possible to depend on cable-modems class because it +// is not yet defined. + { + "name": "second_subnet", + "only-if-required": true, + "test": "member('ALL')", + "option-data": [{ + "name": "dns-servers", + "data": "2001:db8::1" + }] + }, + +// Let's classify all incoming RENEW (message type 5) to a separate +// class. + { + "name": "renews", + "test": "pkt6.msgtype == 5" + }, + +// Let's pick cable modems. In this simple example we'll assume the device +// is a cable modem if it sends a vendor option with enterprise-id equal +// to 4491. + { + "name": "cable-modems", + "test": "vendor.enterprise == 4491" + }, + +// Both a cable modem (by evaluation or host reservation) and has a host +// reservation. + { + "name": "cable-modem-hosts", + "test": "member('cable-modems') and member('KNOWN')" + } + + ], + + +// The following list defines subnets. Each subnet consists of at +// least subnet and pool entries. + "subnet6": [ + { + "pools": [ { "pool": "2001:db8:1::/80" } ], + "subnet": "2001:db8:1::/64", + "client-class": "cable-modems", + "interface": "ethX" + }, +// The following subnet contains a class reservation for a client using +// DUID 01:02:03:04:05:0A:0B:0C:0D:0E. This client will always be assigned +// to this class. + { + "pools": [ { "pool": "2001:db8:2::/80" } ], + "subnet": "2001:db8:2::/64", + "reservations": [ + { + "duid": "01:02:03:04:05:0A:0B:0C:0D:0E", + "client-classes": [ "cable-modems" ] + } ], + "interface": "ethX" + }, +// The following subnet contains a pool with a class constraint: only +// clients which belong to the class are allowed to use this pool. + { + "pools": [ + { + "pool": "2001:db8:3::/80", + "client-class": "cable-modems" + } ], + "subnet": "2001:db8:4::/64", + "interface": "ethY" + } + + ] +}, + +// The following configures logging. It assumes that messages with at +// least informational level (info, warn, error and fatal) should be +// logged to stdout. +"Logging": { + "loggers": [ + { + "name": "kea-dhcp6", + "output_options": [ + { + "output": "stdout" + } + ], + "debuglevel": 0, + "severity": "INFO" + } + ] +} + +} diff --git a/doc/guide/classify.xml b/doc/guide/classify.xml index 0a1b2baf2a..06e7beebf6 100644 --- a/doc/guide/classify.xml +++ b/doc/guide/classify.xml @@ -131,8 +131,10 @@ - Beginning with 1.4 client classes follow now the insertion order - (vs. alphabetical order in previous versions). + Beginning with Kea 1.4.0 release, client classes follow the order + in which they are specified in the configuration + (vs. alphabetical order in previous versions), or for required + classes the order they are required. @@ -184,6 +186,14 @@ belongs to, and the KNOWN class. By convention builtin classes names begin with all caps. + + Currently recognized builtin class names are ALL and KNOWN + and prefixes VENDOR_CLASS_, AFTER_ and EXTERNAL_. The AFTER_ prefix + is a provision for a not yet written hook, the EXTERNAL_ prefix + can be freely used: builtin classes are implicitly defined so + never raise warnings because they do not appear in the configuration. + +
@@ -788,11 +798,11 @@ concatenation of the strings subnet, shared-network or pools are known but output option processing not yet done. The only-if-required flag, false by default, allows to perform the evaluation of the test expression only - when it was required, i.e. in a required-client-classes list. + when it was required, i.e. in a require-client-classes list. - The required-client-classes list which is valid for shared-network, + The require-client-classes list which is valid for shared-network, subnet and pool scope specifies the classes which are evaluated in the second pass before output option processing. The list is built in the reversed precedence order of option diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index 479580e11f..7fed462b7b 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -2064,8 +2064,9 @@ It is merely echoed by the server the DHCP message processing, including the assignment of leases from different pools, the assignment of different options (or different values of the same options) etc. In the current release of the software however, there are - only three mechanisms that take advantage of client classification: - subnet selection, assignment of different options, and, for cable modems, there + only some mechanisms that take advantage of client classification: + private options and option 43 deferred unpacking, subnet selection, + pool selection, assignment of different options, and, for cable modems, there are specific options for use with the TFTP server address and the boot file field. @@ -2091,19 +2092,24 @@ It is merely echoed by the server - The process of doing classification is conducted in five steps. The first step + The process of doing classification is conducted in several steps. 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. - The third step is to assign classes from host reservations. - The forth step is to build the list of required classes and perform - the evaluation for each class of the list. + The third step is to assign classes from host reservations and + evaluate class expressions depending on the "KNOWN" class. + After the list of required classes is built and each class of the list + has its expression evaluated: when it returns true the packet is added + as a member of the class. The last step is to assign options, again possibly based on the class information. + More complete and detailed description is available in + . - There are two methods of doing classification. The first is automatic and relies - on examining the values in the vendor class options. Information from these + There are two main methods of doing classification. The first is automatic and relies + on examining the values in the vendor class options or existence of a + host reservation. Information from these options is extracted and a class name is constructed from it and added to the class list for the packet. The second allows you to specify an expression that is evaluated for each packet. If the result is true the packet is @@ -2155,11 +2161,12 @@ It is merely echoed by the server If there are multiple classes defined and an incoming packet is matched - to multiple classes, the class which is defined first is used. + to multiple classes, the class which is evaluated first is used. - In versions before 1.4 the alphabetical order was used. + In Kea versions prior to 1.4.0 the alphabetical order of class names was used. + Starting from Kea 1.4.0 the classes are ordered as specified in the configuration.
@@ -2260,13 +2267,15 @@ It is merely echoed by the server 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 a new incoming ticket. + evaluated at the reception of a new incoming ticket but + later and only if the class evaluation is required. - The second is the required-client-classes which + The second is the require-client-classes which takes a list of class names and is valid in shared-network, - subnet and pool scope. Classes in these lists are evaluated + subnet and pool scope. Classes in these lists are marked as + required and evaluated after resource assignment and before output option processing. @@ -2279,7 +2288,7 @@ It is merely echoed by the server "client-classes": [ { "name": "Client_foo", - "test": "'' == ''", + "test": "member('ALL')", "only-if-required": true }, ... @@ -2288,7 +2297,7 @@ It is merely echoed by the server { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], - "required-client-classes": [ "Client_foo" ], + "require-client-classes": [ "Client_foo" ], ... }, ... @@ -2296,6 +2305,23 @@ It is merely echoed by the server ... } + + + Required evaluation can be used to express complex dependencies + including for instance subnet membership, and to reverse the + precedence: if you set an option-data in a subnet it takes + precedence over an option-data in a class. When you move the + option-data to a (only-if) required class and require it in + the subnet a class evaluted or set before takes precedence. + + + + Required evaluation is also available at shared-network and + pool levels. The order required classes are considered is + shared-network, subnet and pool, i.e. the opposite order + option-data are processed. + + @@ -3392,7 +3418,7 @@ It is merely echoed by the server to configure the server to assign classes to a client based on the content of the options that this client sends to the server. Host reservations mechanisms also allow for statically assigning classes to the clients. - The definitions of these classes must exist in the Kea + The definitions of these classes should exist in the Kea configuration. The following configuration snippet shows how to specify that a client belongs to classes reserved-class1 and reserved-class2. Those classes are associated with @@ -3438,7 +3464,18 @@ It is merely echoed by the server Static class assignments, as shown above, can be used in conjunction - with classification using expressions. + with classification using expressions. The "KNOWN" builtin class is + added to the packet and any class depending on it directly or indirectly + and not only-if-required is evaluted. + + + + If you want to force the evaluation of a class expression after + the host reservation lookup, for instance because of a dependency on + "reserved-class1" from the previous example, you should add a + "member('KNOWN')" in the expression. + +
diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index ed7e22fa5e..9a671c2def 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -1924,8 +1924,8 @@ should include options from the isc option space: the DHCP message processing, including the assignment of leases from different pools, the assignment of different options (or different values of the same options) etc. In the current release of the software however, there are - only two mechanisms that take advantage of client classification: - subnet selection and assignment of different options. + only some mechanisms that take advantage of client classification: + subnet selection, pool selection, and assignment of different options. @@ -1951,19 +1951,24 @@ should include options from the isc option space: - The process of doing classification is conducted in five steps. The first step + The process of doing classification is conducted in several steps. 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. - The third step is to assign classes from host reservations. - The forth step is to build the list of required classes and perform - the evaluation for each class of the list. + The third step is to assign classes from host reservations and + evaluate class expressions depending on the "KNOWN" class. + After the list of required classes is built and each class of the list + has its expression evaluated: when it returns true the packet is added + as a member of the class. The last step is to assign options again possibly based on the class information. + More complete and detailed description is available in + . - There are two methods of doing classification. The first is automatic and relies - on examining the values in the vendor class options. Information from these + There are two main methods of doing classification. The first is automatic and relies + on examining the values in the vendor class options or existence of a + host reservation. Information from these options is extracted and a class name is constructed from it and added to the class list for the packet. The second allows you to specify an expression that is evaluated for each packet. If the result is true the packet is @@ -2053,13 +2058,15 @@ should include options from the isc option space: 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 a new incoming ticket. + evaluated at the reception of a new incoming ticket but + later and only if the class evaluation is required. - The second is the required-client-classes which + The second is the require-client-classes which takes a list of class names and is valid in shared-network, - subnet and pool scope. Classes in these lists are evaluated + subnet and pool scope. Classes in these lists are marked as + required and evaluated after resource assignment and before output option processing. @@ -2072,7 +2079,7 @@ should include options from the isc option space: "client-classes": [ { "name": "Client_foo", - "test": "'' == ''", + "test": "member('ALL')", "only-if-required": true }, ... @@ -2085,7 +2092,7 @@ should include options from the isc option space: "pool": "2001:db8:1::-2001:db8:1::ffff" } ], - "required-client-classes": [ "Client_foo" ], + "require-client-classes": [ "Client_foo" ], ... }, ... @@ -2093,6 +2100,23 @@ should include options from the isc option space: ... } + + + Required evaluation can be used to express complex dependencies + including for instance subnet membership, and to reverse the + precedence: if you set an option-data in a subnet it takes + precedence over an option-data in a class. When you move the + option-data to a (only-if) required class and require it in + the subnet a class evaluted or set before takes precedence. + + + + Required evaluation is also available at shared-network and + pool/pd-pool levels. The order required classes are considered is + shared-network, subnet and (pd-)pool, i.e. the opposite order + option-data are processed. + +
@@ -2960,7 +2984,18 @@ should include options from the isc option space: Static class assignments, as shown above, can be used in conjunction - with classification using expressions. + with classification using expressions. The "KNOWN" builtin class is + added to the packet and any class depending on it directly or indirectly + and not only-if-required is evaluted. + + + + If you want to force the evaluation of a class expression after + the host reservation lookup, for instance because of a dependency on + "reserved-class1" from the previous example, you should add a + "member('KNOWN')" in the expression. + +
diff --git a/src/bin/dhcp4/dhcp4_lexer.ll b/src/bin/dhcp4/dhcp4_lexer.ll index 1e68cde4ce..74570b2b23 100644 --- a/src/bin/dhcp4/dhcp4_lexer.ll +++ b/src/bin/dhcp4/dhcp4_lexer.ll @@ -795,14 +795,14 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } -\"required-client-classes\" { +\"require-client-classes\" { switch(driver.ctx_) { case isc::dhcp::Parser4Context::SUBNET4: case isc::dhcp::Parser4Context::POOLS: case isc::dhcp::Parser4Context::SHARED_NETWORK: - return isc::dhcp::Dhcp4Parser::make_REQUIRED_CLIENT_CLASSES(driver.loc_); + return isc::dhcp::Dhcp4Parser::make_REQUIRE_CLIENT_CLASSES(driver.loc_); default: - return isc::dhcp::Dhcp4Parser::make_STRING("required-client-classes", driver.loc_); + return isc::dhcp::Dhcp4Parser::make_STRING("require-client-classes", driver.loc_); } } diff --git a/src/bin/dhcp4/dhcp4_parser.yy b/src/bin/dhcp4/dhcp4_parser.yy index 498bddf91e..d503708e29 100644 --- a/src/bin/dhcp4/dhcp4_parser.yy +++ b/src/bin/dhcp4/dhcp4_parser.yy @@ -123,7 +123,7 @@ using namespace std; HOST_RESERVATION_IDENTIFIERS "host-reservation-identifiers" CLIENT_CLASSES "client-classes" - REQUIRED_CLIENT_CLASSES "required-client-classes" + REQUIRE_CLIENT_CLASSES "require-client-classes" TEST "test" ONLY_IF_REQUIRED "only-if-required" CLIENT_CLASS "client-class" @@ -911,7 +911,7 @@ subnet4_param: valid_lifetime | id | rapid_commit | client_class - | required_client_classes + | require_client_classes | reservations | reservation_mode | relay @@ -982,9 +982,9 @@ client_class: CLIENT_CLASS { ctx.leave(); }; -required_client_classes: REQUIRED_CLIENT_CLASSES { +require_client_classes: REQUIRE_CLIENT_CLASSES { ElementPtr c(new ListElement(ctx.loc2pos(@1))); - ctx.stack_.back()->set("required-client-classes", c); + ctx.stack_.back()->set("require-client-classes", c); ctx.stack_.push_back(c); ctx.enter(ctx.NO_KEYWORD); } COLON list_strings { @@ -1061,7 +1061,7 @@ shared_network_param: name | relay | reservation_mode | client_class - | required_client_classes + | require_client_classes | valid_lifetime | unknown_map_entry ; @@ -1344,7 +1344,7 @@ pool_params: pool_param pool_param: pool_entry | option_data_list | client_class - | required_client_classes + | require_client_classes | user_context | unknown_map_entry ; diff --git a/src/bin/dhcp4/tests/classify_unittest.cc b/src/bin/dhcp4/tests/classify_unittest.cc index 504ed6a577..7e9e2d8f32 100644 --- a/src/bin/dhcp4/tests/classify_unittest.cc +++ b/src/bin/dhcp4/tests/classify_unittest.cc @@ -209,7 +209,7 @@ const char* CONFIGS[] = { " \"subnet\": \"10.0.0.0/24\", " " \"id\": 1," " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," - " \"required-client-classes\": [ \"pxe2\" ]" + " \"require-client-classes\": [ \"pxe2\" ]" " } ]" "}" @@ -754,7 +754,7 @@ TEST_F(ClassifyTest, precedencePool) { " \"id\": 1," " \"pools\": [ { " " \"pool\": \"10.0.0.10-10.0.0.100\"," - " \"required-client-classes\": [ \"for-pool\" ]" + " \"require-client-classes\": [ \"for-pool\" ]" " } ]" " } ]" "} ]" @@ -831,10 +831,10 @@ TEST_F(ClassifyTest, precedenceSubnet) { " \"subnet4\": [ { " " \"subnet\": \"10.0.0.0/24\"," " \"id\": 1," - " \"required-client-classes\": [ \"for-subnet\" ]," + " \"require-client-classes\": [ \"for-subnet\" ]," " \"pools\": [ { " " \"pool\": \"10.0.0.10-10.0.0.100\"," - " \"required-client-classes\": [ \"for-pool\" ]" + " \"require-client-classes\": [ \"for-pool\" ]" " } ]" " } ]" "} ]" @@ -908,14 +908,14 @@ TEST_F(ClassifyTest, precedenceNetwork) { "]," "\"shared-networks\": [ {" " \"name\": \"frog\"," - " \"required-client-classes\": [ \"for-network\" ]," + " \"require-client-classes\": [ \"for-network\" ]," " \"subnet4\": [ { " " \"subnet\": \"10.0.0.0/24\"," " \"id\": 1," - " \"required-client-classes\": [ \"for-subnet\" ]," + " \"require-client-classes\": [ \"for-subnet\" ]," " \"pools\": [ { " " \"pool\": \"10.0.0.10-10.0.0.100\"," - " \"required-client-classes\": [ \"for-pool\" ]" + " \"require-client-classes\": [ \"for-pool\" ]" " } ]" " } ]" "} ]" diff --git a/src/bin/dhcp4/tests/parser_unittest.cc b/src/bin/dhcp4/tests/parser_unittest.cc index adadbe9676..eefc89cab1 100644 --- a/src/bin/dhcp4/tests/parser_unittest.cc +++ b/src/bin/dhcp4/tests/parser_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-2018 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 @@ -244,6 +244,7 @@ TEST(ParserTest, file) { "backends.json", "cassandra.json", "classify.json", + "classify2.json", "dhcpv4-over-dhcpv6.json", "hooks.json", "leases-expiration.json", diff --git a/src/bin/dhcp6/dhcp6_lexer.ll b/src/bin/dhcp6/dhcp6_lexer.ll index d8c8afd79d..c9a52e6913 100644 --- a/src/bin/dhcp6/dhcp6_lexer.ll +++ b/src/bin/dhcp6/dhcp6_lexer.ll @@ -1047,15 +1047,15 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } -\"required-client-classes\" { +\"require-client-classes\" { switch(driver.ctx_) { case isc::dhcp::Parser6Context::SUBNET6: case isc::dhcp::Parser6Context::POOLS: case isc::dhcp::Parser6Context::PD_POOLS: case isc::dhcp::Parser6Context::SHARED_NETWORK: - return isc::dhcp::Dhcp6Parser::make_REQUIRED_CLIENT_CLASSES(driver.loc_); + return isc::dhcp::Dhcp6Parser::make_REQUIRE_CLIENT_CLASSES(driver.loc_); default: - return isc::dhcp::Dhcp6Parser::make_STRING("required-client-classes", driver.loc_); + return isc::dhcp::Dhcp6Parser::make_STRING("require-client-classes", driver.loc_); } } diff --git a/src/bin/dhcp6/dhcp6_parser.yy b/src/bin/dhcp6/dhcp6_parser.yy index 9bfaa141d5..ab3f2b3109 100644 --- a/src/bin/dhcp6/dhcp6_parser.yy +++ b/src/bin/dhcp6/dhcp6_parser.yy @@ -116,7 +116,7 @@ using namespace std; HOST_RESERVATION_IDENTIFIERS "host-reservation-identifiers" CLIENT_CLASSES "client-classes" - REQUIRED_CLIENT_CLASSES "required-client-classes" + REQUIRE_CLIENT_CLASSES "require-client-classes" TEST "test" ONLY_IF_REQUIRED "only-if-required" CLIENT_CLASS "client-class" @@ -906,7 +906,7 @@ subnet6_param: preferred_lifetime | id | rapid_commit | client_class - | required_client_classes + | require_client_classes | reservations | reservation_mode | relay @@ -946,9 +946,9 @@ client_class: CLIENT_CLASS { ctx.leave(); }; -required_client_classes: REQUIRED_CLIENT_CLASSES { +require_client_classes: REQUIRE_CLIENT_CLASSES { ElementPtr c(new ListElement(ctx.loc2pos(@1))); - ctx.stack_.back()->set("required-client-classes", c); + ctx.stack_.back()->set("require-client-classes", c); ctx.stack_.push_back(c); ctx.enter(ctx.NO_KEYWORD); } COLON list_strings { @@ -1023,7 +1023,7 @@ shared_network_param: name | relay | reservation_mode | client_class - | required_client_classes + | require_client_classes | preferred_lifetime | rapid_commit | valid_lifetime @@ -1307,7 +1307,7 @@ pool_params: pool_param pool_param: pool_entry | option_data_list | client_class - | required_client_classes + | require_client_classes | user_context | unknown_map_entry ; @@ -1383,7 +1383,7 @@ pd_pool_param: pd_prefix | pd_delegated_len | option_data_list | client_class - | required_client_classes + | require_client_classes | excluded_prefix | excluded_prefix_len | user_context diff --git a/src/bin/dhcp6/tests/classify_unittests.cc b/src/bin/dhcp6/tests/classify_unittests.cc index 573738c15b..99611da61e 100644 --- a/src/bin/dhcp6/tests/classify_unittests.cc +++ b/src/bin/dhcp6/tests/classify_unittests.cc @@ -415,7 +415,7 @@ TEST_F(ClassifyTest, requiredClassification) { "\"subnet6\": [ " "{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], " " \"subnet\": \"2001:db8:1::/48\", " - " \"required-client-classes\": [ \"router\" ], " + " \"require-client-classes\": [ \"router\" ], " " \"interface\": \"eth1\" } ]," "\"client-classes\": [ " "{ \"name\": \"router\", " @@ -1349,7 +1349,7 @@ TEST_F(ClassifyTest, precedencePool) { " \"id\": 1," " \"pools\": [ { " " \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\"," - " \"required-client-classes\": [ \"for-pool\" ]" + " \"require-client-classes\": [ \"for-pool\" ]" " } ]" " } ]" "} ]," @@ -1430,10 +1430,10 @@ TEST_F(ClassifyTest, precedenceSubnet) { " \"subnet6\": [ { " " \"subnet\": \"2001:db8:1::/64\"," " \"id\": 1," - " \"required-client-classes\": [ \"for-subnet\" ]," + " \"require-client-classes\": [ \"for-subnet\" ]," " \"pools\": [ { " " \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\"," - " \"required-client-classes\": [ \"for-pool\" ]" + " \"require-client-classes\": [ \"for-pool\" ]" " } ]" " } ]" "} ]," @@ -1511,14 +1511,14 @@ TEST_F(ClassifyTest, precedenceNetwork) { "\"shared-networks\": [ {" " \"name\": \"frog\"," " \"interface\": \"eth1\"," - " \"required-client-classes\": [ \"for-network\" ]," + " \"require-client-classes\": [ \"for-network\" ]," " \"subnet6\": [ { " " \"subnet\": \"2001:db8:1::/64\"," " \"id\": 1," - " \"required-client-classes\": [ \"for-subnet\" ]," + " \"require-client-classes\": [ \"for-subnet\" ]," " \"pools\": [ { " " \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\"," - " \"required-client-classes\": [ \"for-pool\" ]" + " \"require-client-classes\": [ \"for-pool\" ]" " } ]" " } ]" "} ]," diff --git a/src/bin/dhcp6/tests/parser_unittest.cc b/src/bin/dhcp6/tests/parser_unittest.cc index fdc9c85f38..b221dd760e 100644 --- a/src/bin/dhcp6/tests/parser_unittest.cc +++ b/src/bin/dhcp6/tests/parser_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-2018 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 @@ -248,6 +248,7 @@ TEST(ParserTest, file) { configs.push_back("backends.json"); configs.push_back("cassandra.json"); configs.push_back("classify.json"); + configs.push_back("classify2.json"); configs.push_back("dhcpv4-over-dhcpv6.json"); configs.push_back("duid.json"); configs.push_back("hooks.json"); diff --git a/src/lib/dhcpsrv/network.cc b/src/lib/dhcpsrv/network.cc index 0c48dba822..78b289810e 100644 --- a/src/lib/dhcpsrv/network.cc +++ b/src/lib/dhcpsrv/network.cc @@ -70,7 +70,7 @@ Network::toElement() const { map->set("client-class", Element::create(cclass)); } - // Set required-client-classes + // Set require-client-classes const ClientClasses& classes = getRequiredClasses(); if (!classes.empty()) { ElementPtr class_list = Element::createList(); @@ -78,7 +78,7 @@ Network::toElement() const { it != classes.cend(); ++it) { class_list->add(Element::create(*it)); } - map->set("required-client-classes", class_list); + map->set("require-client-classes", class_list); } // Set renew-timer diff --git a/src/lib/dhcpsrv/network.h b/src/lib/dhcpsrv/network.h index 40ffe93010..dacaa317ca 100644 --- a/src/lib/dhcpsrv/network.h +++ b/src/lib/dhcpsrv/network.h @@ -277,7 +277,7 @@ protected: /// @brief Optional definition of a client class /// /// If defined, only clients belonging to that class will be allowed to use - /// this particular network. The default value for this is an empty list, + /// this particular network. The default value for this is an empty string, /// which means that any client is allowed, regardless of its class. ClientClass client_class_; diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index ce1879f97c..d064b123dc 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -389,7 +389,7 @@ PoolParser::parse(PoolStoragePtr pools, } // Try setting up required client classes. - ConstElementPtr class_list = pool_structure->get("required-client-classes"); + ConstElementPtr class_list = pool_structure->get("require-client-classes"); if (class_list) { const std::vector& classes = class_list->listValue(); for (auto cclass = classes.cbegin(); @@ -712,7 +712,7 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params, } // Try setting up required client classes. - ConstElementPtr class_list = params->get("required-client-classes"); + ConstElementPtr class_list = params->get("require-client-classes"); if (class_list) { const std::vector& classes = class_list->listValue(); for (auto cclass = classes.cbegin(); @@ -891,7 +891,7 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) { client_class_ = client_class; } - ConstElementPtr class_list = pd_pool_->get("required-client-classes"); + ConstElementPtr class_list = pd_pool_->get("require-client-classes"); // Check the pool parameters. It will throw an exception if any // of the required parameters are invalid. @@ -1112,7 +1112,7 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params, } // Try setting up required client classes. - ConstElementPtr class_list = params->get("required-client-classes"); + ConstElementPtr class_list = params->get("require-client-classes"); if (class_list) { const std::vector& classes = class_list->listValue(); for (auto cclass = classes.cbegin(); diff --git a/src/lib/dhcpsrv/parsers/shared_network_parser.cc b/src/lib/dhcpsrv/parsers/shared_network_parser.cc index 5a04e6cd20..7270699002 100644 --- a/src/lib/dhcpsrv/parsers/shared_network_parser.cc +++ b/src/lib/dhcpsrv/parsers/shared_network_parser.cc @@ -70,9 +70,9 @@ SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data) { } } - if (shared_network_data->contains("required-client-classes")) { + if (shared_network_data->contains("require-client-classes")) { const std::vector& class_list = - shared_network_data->get("required-client-classes")->listValue(); + shared_network_data->get("require-client-classes")->listValue(); for (auto cclass = class_list.cbegin(); cclass != class_list.cend(); ++cclass) { if (((*cclass)->getType() != Element::string) || @@ -125,9 +125,9 @@ SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data) { } } - if (shared_network_data->contains("required-client-classes")) { + if (shared_network_data->contains("require-client-classes")) { const std::vector& class_list = - shared_network_data->get("required-client-classes")->listValue(); + shared_network_data->get("require-client-classes")->listValue(); for (auto cclass = class_list.cbegin(); cclass != class_list.cend(); ++cclass) { if (((*cclass)->getType() != Element::string) || diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc index 2e96ee1ded..e2d239938d 100644 --- a/src/lib/dhcpsrv/pool.cc +++ b/src/lib/dhcpsrv/pool.cc @@ -27,14 +27,7 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const { } bool Pool::clientSupported(const ClientClasses& classes) const { - if (client_class_.empty()) { - // There is no class defined for this pool, so we do - // support everyone. - return (true); - } else if (classes.contains(client_class_)) { - return (true); - } - return (false); + return (client_class_.empty() || classes.contains(client_class_)); } void Pool::allowClientClass(const ClientClass& class_name) { @@ -112,7 +105,7 @@ Pool::toElement() const { map->set("client-class", Element::create(cclass)); } - // Set required-client-classes + // Set require-client-classes const ClientClasses& classes = getRequiredClasses(); if (!classes.empty()) { ElementPtr class_list =Element::createList(); @@ -120,7 +113,7 @@ Pool::toElement() const { it != classes.cend(); ++it) { class_list->add(Element::create(*it)); } - map->set("required-client-classes", class_list); + map->set("require-client-classes", class_list); } return (map); diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index 8f8c691d87..cb6a96626c 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -802,7 +802,7 @@ TEST(CfgSubnets4Test, unparseSubnet) { " \"reservation-mode\": \"all\",\n" " \"option-data\": [ ],\n" " \"pools\": [ ]\n," - " \"required-client-classes\": [ \"foo\", \"bar\" ]\n" + " \"require-client-classes\": [ \"foo\", \"bar\" ]\n" "} ]\n"; runToElementTest(expected, cfg); } @@ -848,7 +848,7 @@ TEST(CfgSubnets4Test, unparsePool) { " \"option-data\": [ ],\n" " \"pool\": \"192.0.2.64/26\",\n" " \"client-class\": \"bar\",\n" - " \"required-client-classes\": [ \"foo\" ]\n" + " \"require-client-classes\": [ \"foo\" ]\n" " }\n" " ]\n" "} ]\n"; diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index 6d1f6baad4..227c60e175 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -490,7 +490,7 @@ TEST(CfgSubnets6Test, unparseSubnet) { " \"pools\": [ ],\n" " \"pd-pools\": [ ],\n" " \"option-data\": [ ],\n" - " \"required-client-classes\": [ \"foo\", \"bar\" ]\n" + " \"require-client-classes\": [ \"foo\", \"bar\" ]\n" "} ]\n"; runToElementTest(expected, cfg); } @@ -533,7 +533,7 @@ TEST(CfgSubnets6Test, unparsePool) { " \"pool\": \"2001:db8:1:1::/64\",\n" " \"option-data\": [ ],\n" " \"client-class\": \"bar\",\n" - " \"required-client-classes\": [ \"foo\" ]\n" + " \"require-client-classes\": [ \"foo\" ]\n" " }\n" " ],\n" " \"pd-pools\": [ ],\n" @@ -580,7 +580,7 @@ TEST(CfgSubnets6Test, unparsePdPool) { " \"prefix-len\": 48,\n" " \"delegated-len\": 64,\n" " \"option-data\": [ ],\n" - " \"required-client-classes\": [ \"bar\" ]\n" + " \"require-client-classes\": [ \"bar\" ]\n" " },{\n" " \"prefix\": \"2001:db8:3::\",\n" " \"prefix-len\": 48,\n" diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc index a0e113668b..494e40663b 100644 --- a/src/lib/dhcpsrv/tests/pool_unittest.cc +++ b/src/lib/dhcpsrv/tests/pool_unittest.cc @@ -230,7 +230,7 @@ TEST(Pool4Test, clientClass) { EXPECT_TRUE(pool->clientSupported(three_classes)); } -// This test checks that handling for required-client-classes is valid. +// This test checks that handling for require-client-classes is valid. TEST(Pool4Test, requiredClasses) { // Create a pool. Pool4Ptr pool(new Pool4(IOAddress("192.0.2.0"), @@ -625,7 +625,7 @@ TEST(Pool6Test, clientClass) { EXPECT_TRUE(pool.clientSupported(three_classes)); } -// This test checks that handling for required-client-classes is valid. +// This test checks that handling for require-client-classes is valid. TEST(Pool6Test, requiredClasses) { // Create a pool. Pool6 pool(Lease::TYPE_NA, IOAddress("2001:db8::1"), diff --git a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc index dfb3a9db9c..193428ac24 100644 --- a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc @@ -52,7 +52,7 @@ public: " \"server-hostname\": \"\"," " \"boot-file-name\": \"\"," " \"client-class\": \"\"," - " \"required-client-classes\": []\n," + " \"require-client-classes\": []\n," " \"reservation-mode\": \"all\"," " \"4o6-interface\": \"\"," " \"4o6-interface-id\": \"\"," @@ -73,7 +73,7 @@ public: " \"server-hostname\": \"\"," " \"boot-file-name\": \"\"," " \"client-class\": \"\"," - " \"required-client-classes\": []\n," + " \"require-client-classes\": []\n," " \"reservation-mode\": \"all\"," " \"4o6-interface\": \"\"," " \"4o6-interface-id\": \"\"," @@ -194,7 +194,7 @@ public: " \"preferred-lifetime\": 300," " \"valid-lifetime\": 400," " \"client-class\": \"\"," - " \"required-client-classes\": []\n," + " \"require-client-classes\": []\n," " \"reservation-mode\": \"all\"," " \"decline-probation-period\": 86400," " \"dhcp4o6-port\": 0," @@ -210,7 +210,7 @@ public: " \"preferred-lifetime\": 30," " \"valid-lifetime\": 40," " \"client-class\": \"\"," - " \"required-client-classes\": []\n," + " \"require-client-classes\": []\n," " \"reservation-mode\": \"all\"," " \"decline-probation-period\": 86400," " \"dhcp4o6-port\": 0," @@ -283,7 +283,7 @@ TEST_F(SharedNetwork6ParserTest, clientClass) { EXPECT_EQ("alpha", network->getClientClass()); } -// This test verifies that it's possible to specify required-client-classes +// This test verifies that it's possible to specify require-client-classes // on shared-network level. TEST_F(SharedNetwork6ParserTest, evalClientClasses) { std::string config = getWorkingConfig(); @@ -292,7 +292,7 @@ TEST_F(SharedNetwork6ParserTest, evalClientClasses) { ElementPtr class_list = Element::createList(); class_list->add(Element::create("alpha")); class_list->add(Element::create("beta")); - config_element->set("required-client-classes", class_list); + config_element->set("require-client-classes", class_list); // Parse configuration specified above. SharedNetwork6Parser parser; @@ -305,7 +305,7 @@ TEST_F(SharedNetwork6ParserTest, evalClientClasses) { EXPECT_EQ("alpha, beta", classes.toText()); } -// This test verifies that bad required-client-classes configs raise +// This test verifies that bad require-client-classes configs raise // expected errors. TEST_F(SharedNetwork6ParserTest, badEvalClientClasses) { std::string config = getWorkingConfig(); @@ -315,7 +315,7 @@ TEST_F(SharedNetwork6ParserTest, badEvalClientClasses) { ElementPtr class_list = Element::createList(); class_list->add(Element::create("alpha")); class_list->add(Element::create(1234)); - config_element->set("required-client-classes", class_list); + config_element->set("require-client-classes", class_list); // Parse configuration specified above. SharedNetwork6Parser parser; diff --git a/src/lib/dhcpsrv/tests/shared_network_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_unittest.cc index aefe3bc398..9b74d3a591 100644 --- a/src/lib/dhcpsrv/tests/shared_network_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_unittest.cc @@ -258,7 +258,7 @@ TEST(SharedNetwork4Test, unparse) { " \"valid-lifetime\": 30\n" " }\n" " ],\n" - " \"required-client-classes\": [ \"foo\" ],\n" + " \"require-client-classes\": [ \"foo\" ],\n" " \"valid-lifetime\": 200\n" "}\n"; @@ -538,7 +538,7 @@ TEST(SharedNetwork6Test, unparse) { " \"valid-lifetime\": 40\n" " }\n" " ],\n" - " \"required-client-classes\": [ \"foo\" ],\n" + " \"require-client-classes\": [ \"foo\" ],\n" " \"valid-lifetime\": 300\n" "}\n"; diff --git a/src/lib/eval/eval.dox b/src/lib/eval/eval.dox index db29baa9aa..64efbb4c87 100644 --- a/src/lib/eval/eval.dox +++ b/src/lib/eval/eval.dox @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2018 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 @@ -22,10 +22,10 @@ EvalContext::expression. Parameters to the @ref isc::eval::EvalContext class constructor are - the universe to choose between DHCPv4 and DHCPv6 for dependent expressions, - and a closure which checks if a client class is already known used + the universe to choose between DHCPv4 and DHCPv6 for DHCP version + dependent expressions, and a function used by the parser to accept only already known or built-in client - class names in client class membership expressions. This closure defaults + class names in client class membership expressions. This function defaults to accept all client class names. Internally, the parser code is generated by flex and bison. These two diff --git a/src/lib/eval/eval_context.h b/src/lib/eval/eval_context.h index 7690db1504..dac140510d 100644 --- a/src/lib/eval/eval_context.h +++ b/src/lib/eval/eval_context.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2018 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 @@ -41,17 +41,19 @@ public: PARSER_STRING ///< expression is expected to evaluate to string } ParserType; + /// @brief Type of the check known function. + typedef std::function CheckKnown; /// @brief Default constructor. /// /// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used /// by the parser to determine which option definitions set should be used /// to map option names to option codes. - /// @param check_known A closure called to check if a client class + /// @param check_known A function called to check if a client class /// used for membership is already known. If it is not the parser /// will fail: only backward or built-in references are accepted. EvalContext(const Option::Universe& option_universe, - std::function check_known = acceptAll); + CheckKnown check_known = acceptAll); /// @brief destructor virtual ~EvalContext(); @@ -198,8 +200,8 @@ public: /// set should be used to map option name to option code. Option::Universe option_universe_; - /// @brief Closure to check if a client class is already known - std::function check_known_; + /// @brief Function to check if a client class is already known + CheckKnown check_known_; };