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
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
--- /dev/null
+// 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"
+ }
+ ]
+}
+
+}
--- /dev/null
+// 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"
+ }
+ ]
+}
+
+}
<note>
<para>
- 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.
</para>
</note>
belongs to, and the KNOWN class. By convention builtin classes
names begin with all caps.
</para>
+
+ <para>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.
+ </para>
+
</section>
<section id="classification-using-expressions">
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.
</para>
<para>
- 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
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.
</para>
</para>
<para>
- 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
+ <xref linkend="classify"/>.
</para>
<para>
- 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
<para>
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.
</para>
<note><para>
- 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.
</para></note>
</section>
The first one is the per-class <command>only-if-required</command>
flag which is false by default. When it is set to
<command>true</command> 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.
</para>
<para>
- The second is the <command>required-client-classes</command> which
+ The second is the <command>require-client-classes</command> 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.
</para>
"client-classes": [
{<userinput>
"name": "Client_foo",
- "test": "'' == ''",
+ "test": "member('ALL')",
"only-if-required": true</userinput>
},
...
{
"subnet": "192.0.2.0/24",
"pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
- <userinput>"required-client-classes": [ "Client_foo" ],</userinput>
+ <userinput>"require-client-classes": [ "Client_foo" ],</userinput>
...
},
...
...
}</screen>
</para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
</section>
</section>
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 <command>reserved-class1</command>
and <command>reserved-class2</command>. Those classes are associated with
</screen>
<para>Static class assignments, as shown above, can be used in conjunction
- with classification using expressions.</para>
+ 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.
+ </para>
+
+ <note>
+ <para>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.</para>
+ </note>
+
</section>
<section id="reservations4-mysql-pgsql">
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.
</para>
<para>
</para>
<para>
- 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
+ <xref linkend="classify"/>.
</para>
<para>
- 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
The first one is the per-class <command>only-if-required</command>
flag which is false by default. When it is set to
<command>true</command> 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.
</para>
<para>
- The second is the <command>required-client-classes</command> which
+ The second is the <command>require-client-classes</command> 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.
</para>
"client-classes": [
{<userinput>
"name": "Client_foo",
- "test": "'' == ''",
+ "test": "member('ALL')",
"only-if-required": true</userinput>
},
...
"pool": "2001:db8:1::-2001:db8:1::ffff"
}
],
- <userinput>"required-client-classes": [ "Client_foo" ],</userinput>
+ <userinput>"require-client-classes": [ "Client_foo" ],</userinput>
...
},
...
...
}</screen>
</para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
</section>
</section>
</screen>
<para>Static class assignments, as shown above, can be used in conjunction
- with classification using expressions.</para>
+ 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.
+ </para>
+
+ <note>
+ <para>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.</para>
+ </note>
+
</section>
<section id="reservations6-mysql-pgsql">
}
}
-\"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_);
}
}
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"
| id
| rapid_commit
| client_class
- | required_client_classes
+ | require_client_classes
| reservations
| reservation_mode
| relay
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 {
| relay
| reservation_mode
| client_class
- | required_client_classes
+ | require_client_classes
| valid_lifetime
| unknown_map_entry
;
pool_param: pool_entry
| option_data_list
| client_class
- | required_client_classes
+ | require_client_classes
| user_context
| unknown_map_entry
;
" \"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\" ]"
" } ]"
"}"
" \"id\": 1,"
" \"pools\": [ { "
" \"pool\": \"10.0.0.10-10.0.0.100\","
- " \"required-client-classes\": [ \"for-pool\" ]"
+ " \"require-client-classes\": [ \"for-pool\" ]"
" } ]"
" } ]"
"} ]"
" \"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\" ]"
" } ]"
" } ]"
"} ]"
"],"
"\"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\" ]"
" } ]"
" } ]"
"} ]"
-// 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
"backends.json",
"cassandra.json",
"classify.json",
+ "classify2.json",
"dhcpv4-over-dhcpv6.json",
"hooks.json",
"leases-expiration.json",
}
}
-\"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_);
}
}
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"
| id
| rapid_commit
| client_class
- | required_client_classes
+ | require_client_classes
| reservations
| reservation_mode
| relay
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 {
| relay
| reservation_mode
| client_class
- | required_client_classes
+ | require_client_classes
| preferred_lifetime
| rapid_commit
| valid_lifetime
pool_param: pool_entry
| option_data_list
| client_class
- | required_client_classes
+ | require_client_classes
| user_context
| unknown_map_entry
;
| pd_delegated_len
| option_data_list
| client_class
- | required_client_classes
+ | require_client_classes
| excluded_prefix
| excluded_prefix_len
| user_context
"\"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\", "
" \"id\": 1,"
" \"pools\": [ { "
" \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\","
- " \"required-client-classes\": [ \"for-pool\" ]"
+ " \"require-client-classes\": [ \"for-pool\" ]"
" } ]"
" } ]"
"} ],"
" \"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\" ]"
" } ]"
" } ]"
"} ],"
"\"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\" ]"
" } ]"
" } ]"
"} ],"
-// 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
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");
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();
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
/// @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_;
}
// 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<data::ElementPtr>& classes = class_list->listValue();
for (auto cclass = classes.cbegin();
}
// 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<data::ElementPtr>& classes = class_list->listValue();
for (auto cclass = classes.cbegin();
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.
}
// 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<data::ElementPtr>& classes = class_list->listValue();
for (auto cclass = classes.cbegin();
}
}
- if (shared_network_data->contains("required-client-classes")) {
+ if (shared_network_data->contains("require-client-classes")) {
const std::vector<data::ElementPtr>& 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) ||
}
}
- if (shared_network_data->contains("required-client-classes")) {
+ if (shared_network_data->contains("require-client-classes")) {
const std::vector<data::ElementPtr>& 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) ||
}
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) {
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();
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);
" \"reservation-mode\": \"all\",\n"
" \"option-data\": [ ],\n"
" \"pools\": [ ]\n,"
- " \"required-client-classes\": [ \"foo\", \"bar\" ]\n"
+ " \"require-client-classes\": [ \"foo\", \"bar\" ]\n"
"} ]\n";
runToElementTest<CfgSubnets4>(expected, cfg);
}
" \"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";
" \"pools\": [ ],\n"
" \"pd-pools\": [ ],\n"
" \"option-data\": [ ],\n"
- " \"required-client-classes\": [ \"foo\", \"bar\" ]\n"
+ " \"require-client-classes\": [ \"foo\", \"bar\" ]\n"
"} ]\n";
runToElementTest<CfgSubnets6>(expected, cfg);
}
" \"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"
" \"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"
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"),
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"),
" \"server-hostname\": \"\","
" \"boot-file-name\": \"\","
" \"client-class\": \"\","
- " \"required-client-classes\": []\n,"
+ " \"require-client-classes\": []\n,"
" \"reservation-mode\": \"all\","
" \"4o6-interface\": \"\","
" \"4o6-interface-id\": \"\","
" \"server-hostname\": \"\","
" \"boot-file-name\": \"\","
" \"client-class\": \"\","
- " \"required-client-classes\": []\n,"
+ " \"require-client-classes\": []\n,"
" \"reservation-mode\": \"all\","
" \"4o6-interface\": \"\","
" \"4o6-interface-id\": \"\","
" \"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,"
" \"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,"
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();
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;
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();
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;
" \"valid-lifetime\": 30\n"
" }\n"
" ],\n"
- " \"required-client-classes\": [ \"foo\" ],\n"
+ " \"require-client-classes\": [ \"foo\" ],\n"
" \"valid-lifetime\": 200\n"
"}\n";
" \"valid-lifetime\": 40\n"
" }\n"
" ],\n"
- " \"required-client-classes\": [ \"foo\" ],\n"
+ " \"require-client-classes\": [ \"foo\" ],\n"
" \"valid-lifetime\": 300\n"
"}\n";
-// 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
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
-// 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
PARSER_STRING ///< expression is expected to evaluate to string
} ParserType;
+ /// @brief Type of the check known function.
+ typedef std::function<bool(const ClientClass&)> 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<bool(const ClientClass&)> check_known = acceptAll);
+ CheckKnown check_known = acceptAll);
/// @brief destructor
virtual ~EvalContext();
/// 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<bool(const ClientClass&)> check_known_;
+ /// @brief Function to check if a client class is already known
+ CheckKnown check_known_;
};