mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-23 02:17:33 +00:00
914 lines
42 KiB
ReStructuredText
914 lines
42 KiB
ReStructuredText
.. _classify:
|
|
|
|
*********************
|
|
Client Classification
|
|
*********************
|
|
|
|
Client Classification Overview
|
|
==============================
|
|
|
|
In certain cases it is useful to differentiate among different types
|
|
of clients and treat them accordingly. Common reasons include:
|
|
|
|
- The clients represent different pieces of topology, e.g. a cable
|
|
modem is not the same as the clients behind that modem.
|
|
|
|
- The clients have different behavior, e.g. a smartphone behaves
|
|
differently from a laptop.
|
|
|
|
- The clients require different values for some options, e.g. a
|
|
docsis3.0 cable modem requires different settings from a docsis2.0
|
|
cable modem.
|
|
|
|
To make management easier, different clients can be grouped into a
|
|
client class to receive common options.
|
|
|
|
An incoming packet can be associated with a client class in several
|
|
ways:
|
|
|
|
- Implicitly, using a vendor class option or another built-in condition.
|
|
|
|
- Using an expression which evaluates to true.
|
|
|
|
- Using static host reservations, a shared network, a subnet, etc.
|
|
|
|
- Using a hook.
|
|
|
|
It is envisaged that client classification will be used to change the
|
|
behavior of almost any part of the DHCP message processing. There are
|
|
currently five mechanisms that take advantage of client classification:
|
|
subnet selection, pool selection, definition of DHCPv4 private (codes
|
|
224-254) and code 43 options, assignment of different options, and, for
|
|
DHCPv4 cable modems, the setting of specific options for use with the
|
|
TFTP server address and the boot file field.
|
|
|
|
The classification process is conducted in several steps:
|
|
|
|
1. The ALL class is associated with the incoming packet.
|
|
|
|
2. Vendor class options are processed.
|
|
|
|
3. Classes with matching expressions and not marked for later evaluation ("on
|
|
request" or depending on the KNOWN/UNKNOWN built-in classes)
|
|
are processed in the order they are defined in the
|
|
configuration; the boolean expression is evaluated and, if it
|
|
returns true ("match"), the incoming packet is associated with the
|
|
class.
|
|
|
|
4. If a private or code 43 DHCPv4 option is received, it is decoded
|
|
following its client class or global (or, for option 43, last
|
|
resort) definition.
|
|
|
|
5. A subnet is chosen, possibly based on the class information when
|
|
some subnets are reserved. More precisely: when choosing a subnet,
|
|
the server iterates over all of the subnets that are feasible given
|
|
the information found in the packet (client address, relay address,
|
|
etc.). It uses the first subnet it finds that either doesn't have a
|
|
class associated with it, or has a class which matches one of the
|
|
packet's classes.
|
|
|
|
6. The server looks for host reservations. If an identifier from the
|
|
incoming packet matches a host reservation in the subnet or shared
|
|
network, the packet is associated with the KNOWN class and all
|
|
classes of the host reservation. If a reservation is not found, the
|
|
packet is assigned to the UNKNOWN class.
|
|
|
|
7. Classes with matching expressions - directly, or indirectly using the
|
|
KNOWN/UNKNOWN built-in classes and not marked for later evaluation ("on
|
|
request") - are processed in the order they are defined
|
|
in the configuration; the boolean expression is evaluated and, if it
|
|
returns true ("match"), the incoming packet is associated with the
|
|
class. After a subnet is selected, the server determines whether
|
|
there is a reservation for a given client. Therefore, it is not
|
|
possible to use KNOWN/UNKNOWN classes to select a shared network or
|
|
a subnet.
|
|
|
|
8. If needed, addresses and prefixes from pools are assigned, possibly
|
|
based on the class information when some pools are reserved for
|
|
class members.
|
|
|
|
9. Classes marked as "required" are evaluated in the order in which
|
|
they are listed: first the shared network, then the subnet, and
|
|
finally the pools that assigned resources belong to.
|
|
|
|
10. Options are assigned, again possibly based on the class information
|
|
in the order that classes were associated with the incoming packet.
|
|
For DHCPv4 private and code 43 options, this includes class local
|
|
option definitions.
|
|
|
|
..
|
|
|
|
.. note::
|
|
|
|
Client classes in Kea follow the order in which they are specified in
|
|
the configuration (vs. alphabetical order). Required classes follow
|
|
the order in which they are required.
|
|
|
|
When determining which options to include in the response, the server
|
|
examines the union of options from all of the assigned classes. If two
|
|
or more classes include the same option, the value from the first class
|
|
examined is used; classes are examined in the order they were
|
|
associated, so ALL is always the first class and matching required
|
|
classes are last.
|
|
|
|
As an example, imagine that an incoming packet matches two classes.
|
|
Class "foo" defines values for an NTP server (option 42 in DHCPv4) and
|
|
an SMTP server (option 69 in DHCPv4), while class "bar" defines values
|
|
for an NTP server and a POP3 server (option 70 in DHCPv4). The server
|
|
examines the three options - NTP, SMTP, and POP3 - and returns any that
|
|
the client requested. As the NTP server was defined twice, the server
|
|
chooses only one of the values for the reply; the class from which the
|
|
value is obtained is unspecified.
|
|
|
|
.. note::
|
|
|
|
Care should be taken with client classification, as it is easy for
|
|
clients that do not meet any class criteria to be denied service
|
|
altogether.
|
|
|
|
.. _classification-using-vendor:
|
|
|
|
Built-in Client Classes
|
|
=======================
|
|
|
|
Some classes are built-in, so they do not need to be defined. The main
|
|
example uses Vendor Class information: the server checks whether an
|
|
incoming DHCPv4 packet includes the vendor class identifier option (60)
|
|
or an incoming DHCPv6 packet includes the vendor class option (16). If
|
|
it does, the content of that option is prepended with "VENDOR_CLASS\_"
|
|
and the result is interpreted as a class. For example, modern cable
|
|
modems send this option with value "docsis3.0", so the packet belongs to
|
|
class "VENDOR_CLASS_docsis3.0".
|
|
|
|
The "HA\_" prefix is used by the High Availability hooks library to
|
|
designate certain servers to process DHCP packets as a result of load
|
|
balancing. The class name is constructed by prepending the "HA\_" prefix
|
|
to the name of the server which should process the DHCP packet. This
|
|
server uses an appropriate pool or subnet to allocate IP addresses
|
|
(and/or prefixes), based on the assigned client classes. The details can
|
|
be found in :ref:`high-availability-library`.
|
|
|
|
Other examples are the ALL class, which all incoming packets belong to,
|
|
and the KNOWN class, assigned when host reservations exist for a
|
|
particular client. By convention, built-in classes' names begin with all
|
|
capital letters.
|
|
|
|
Currently recognized built-in class names are ALL, KNOWN and UNKNOWN, and the
|
|
prefixes VENDOR_CLASS\_, HA\_, AFTER\_, and EXTERNAL\_. Although the AFTER\_
|
|
prefix is a provision for an as-yet-unwritten hook, the EXTERNAL\_
|
|
prefix can be freely used; built-in classes are implicitly defined so
|
|
they never raise warnings if they do not appear in the configuration.
|
|
|
|
.. _classification-using-expressions:
|
|
|
|
Using Expressions in Classification
|
|
===================================
|
|
|
|
The expression portion of a classification definition contains operators
|
|
and values. All values are currently strings; operators take a string or
|
|
strings and return another string. When all the operations have
|
|
completed, the result should be a value of "true" or "false". The packet
|
|
belongs to the class (and the class name is added to the list of
|
|
classes) if the result is "true". Expressions are written in standard
|
|
format and can be nested.
|
|
|
|
Expressions are pre-processed during the parsing of the configuration
|
|
file and converted to an internal representation. This allows certain
|
|
types of errors to be caught and logged during parsing. Examples of
|
|
these errors include an incorrect number or type of argument to an
|
|
operator. The evaluation code also checks for this class of error and
|
|
generally throws an exception, though this should not occur in a
|
|
normally functioning system.
|
|
|
|
Other issues, such as the starting position of a substring being
|
|
outside of the substring or an option not existing in the packet, result
|
|
in the operator returning an empty string.
|
|
|
|
Dependencies between classes are also checked. For instance, forward
|
|
dependencies are rejected when the configuration is parsed; an
|
|
expression can only depend on already-defined classes (including built-in
|
|
classes) which are evaluated in a previous or the same evaluation phase.
|
|
This does not apply to the KNOWN or UNKNOWN classes.
|
|
|
|
.. table:: List of Classification Values
|
|
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Name | Example expression | Example value |
|
|
+=======================+===============================+=======================+
|
|
| String literal | 'example' | 'example' |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Hexadecimal string | 0x5a7d | 'Z}' |
|
|
| literal | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| IP address literal | 10.0.0.1 | 0x0a000001 |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Integer literal | 123 | '123' |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Binary content of the | option[123].hex | '(content of the |
|
|
| option | | option)' |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Option existence | option[123].exists | 'true' |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Client class | member('foobar') | 'true' |
|
|
| membership | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Known client | known | member('KNOWN') |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Unknown client | unknown | not member('KNOWN') |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| DHCPv4 relay agent | relay4[123].hex | '(content of the RAI |
|
|
| sub-option | | sub-option)' |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| DHCPv6 Relay Options | relay6[nest].option[code].hex | (value of the option) |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| DHCPv6 Relay Peer | relay6[nest].peeraddr | 2001:DB8::1 |
|
|
| Address | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| DHCPv6 Relay Link | relay6[nest].linkaddr | 2001:DB8::1 |
|
|
| Address | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Interface name of | pkt.iface | eth0 |
|
|
| packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Source address of | pkt.src | 10.1.2.3 |
|
|
| packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Destination address | pkt.dst | 10.1.2.3 |
|
|
| of packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Length of packet | pkt.len | 513 |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Hardware address in | pkt4.mac | 0x010203040506 |
|
|
| DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Hardware length in | pkt4.hlen | 6 |
|
|
| DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Hardware type in | pkt4.htype | 6 |
|
|
| DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| ciaddr field in | pkt4.ciaddr | 192.0.2.1 |
|
|
| DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| giaddr field in | pkt4.giaddr | 192.0.2.1 |
|
|
| DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| yiaddr field in | pkt4.yiaddr | 192.0.2.1 |
|
|
| DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| siaddr field in | pkt4.siaddr | 192.0.2.1 |
|
|
| DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Message type in | pkt4.msgtype | 1 |
|
|
| DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Transaction ID (xid) | pkt4.transid | 12345 |
|
|
| in DHCPv4 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Message type in | pkt6.msgtype | 1 |
|
|
| DHCPv6 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Transaction ID in | pkt6.transid | 12345 |
|
|
| DHCPv6 packet | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Vendor option | vendor[*].exists | true |
|
|
| existence (any | | |
|
|
| vendor) | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Vendor option | vendor[4491].exists | true |
|
|
| existence (specific | | |
|
|
| vendor) | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Enterprise-id from | vendor.enterprise | 4491 |
|
|
| vendor option | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Vendor sub-option | vendor[4491].option[1].exists | true |
|
|
| existence | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Vendor sub-option | vendor[4491].option[1].hex | docsis3.0 |
|
|
| content | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Vendor class option | vendor-class[*].exist | true |
|
|
| existence (any | s | |
|
|
| vendor) | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Vendor class option | vendor-class[4491].exists | true |
|
|
| existence (specific | | |
|
|
| vendor) | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Enterprise-id from | vendor-class.enterprise | 4491 |
|
|
| vendor class option | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| First data chunk from | vendor-class[4491].data | docsis3.0 |
|
|
| vendor class option | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
| Specific data chunk | vendor-class[4491].data[3] | docsis3.0 |
|
|
| from vendor class | | |
|
|
| option | | |
|
|
+-----------------------+-------------------------------+-----------------------+
|
|
|
|
Notes:
|
|
|
|
- Hexadecimal strings are converted into a string as expected. The
|
|
starting "0X" or "0x" is removed, and if the string is an odd number
|
|
of characters a "0" is prepended to it.
|
|
|
|
- IP addresses are converted into strings of length 4 or 16. IPv4,
|
|
IPv6, and IPv4-embedded IPv6 (e.g. IPv4-mapped IPv6) addresses are
|
|
supported.
|
|
|
|
- Integers in an expression are converted to 32-bit unsigned integers
|
|
and are represented as four-byte strings; for example, 123 is
|
|
represented as 0x0000007b. All expressions that return numeric values
|
|
use 32-bit unsigned integers, even if the field in the packet is
|
|
smaller. In general, it is easier to use decimal notation to
|
|
represent integers, but it is also possible to use hexadecimal
|
|
notation. When writing an integer in hexadecimal, care should be
|
|
taken to make sure the value is represented as 32 bits, e.g. use
|
|
0x00000001 instead of 0x1 or 0x01. Also, make sure the value is
|
|
specified in network order, e.g. 1 is represented as 0x00000001.
|
|
|
|
- "option[code].hex" extracts the value of the option with the code
|
|
"code" from the incoming packet. If the packet doesn't contain the
|
|
option, it returns an empty string. The string is presented as a byte
|
|
string of the option payload, without the type code or length fields.
|
|
|
|
- "option[code].exists" checks whether an option with the code "code"
|
|
is present in the incoming packet. It can be used with empty options.
|
|
|
|
- "member('foobar')" checks whether the packet belongs to the client
|
|
class "foobar". To avoid dependency loops, the configuration file
|
|
parser verifies whether client classes were already defined or are
|
|
built-in, i.e., beginning by "VENDOR_CLASS\_", "AFTER\_" (for the
|
|
to-come "after" hook) and "EXTERNAL\_" or equal to "ALL", "KNOWN",
|
|
"UNKNOWN", etc.
|
|
|
|
"known" and "unknown" are shorthand for "member('KNOWN')" and "not
|
|
member('KNOWN')". Note that the evaluation of any expression using
|
|
directly or indirectly the "KNOWN" class is deferred after the host
|
|
reservation lookup (i.e. when the "KNOWN" or "UNKNOWN" partition is
|
|
determined).
|
|
|
|
- "relay4[code].hex" attempts to extract the value of the sub-option
|
|
"code" from the option inserted as the DHCPv4 Relay Agent Information
|
|
(82) option. If the packet doesn't contain a RAI option, or the RAI
|
|
option doesn't contain the requested sub-option, the expression
|
|
returns an empty string. The string is presented as a byte string of
|
|
the option payload without the type code or length fields. This
|
|
expression is allowed in DHCPv4 only.
|
|
|
|
- "relay4" shares the same representation types as "option"; for
|
|
instance, "relay4[code].exists" is supported.
|
|
|
|
- "relay6[nest]" allows access to the encapsulations used by any DHCPv6
|
|
relays that forwarded the packet. The "nest" level specifies the
|
|
relay from which to extract the information, with a value of 0
|
|
indicating the relay closest to the DHCPv6 server. Negative values
|
|
allow specifying relays counted from the DHCPv6 client, -1 indicating
|
|
the relay closest to the client. In general, negative "nest" level is
|
|
the same as the number of relays + "nest" level. If the requested
|
|
encapsulation doesn't exist, an empty string "" is returned. This
|
|
expression is allowed in DHCPv6 only.
|
|
|
|
- "relay6[nest].option[code]" shares the same representation types as
|
|
"option"; for instance, "relay6[nest].option[code].exists" is
|
|
supported.
|
|
|
|
- Expressions starting with "pkt4" can be used only in DHCPv4. They
|
|
allow access to DHCPv4 message fields.
|
|
|
|
- "pkt6" refers to information from the client request. To access any
|
|
information from an intermediate relay use "relay6". "pkt6.msgtype"
|
|
and "pkt6.transid" output a 4-byte binary string for the message type
|
|
or transaction id. For example the message type SOLICIT will be
|
|
"0x00000001" or simply 1 as in "pkt6.msgtype == 1".
|
|
|
|
- Vendor option means the Vendor-Identifying Vendor-Specific Information
|
|
option in DHCPv4 (code 125; see `Section 4 of RFC
|
|
3925 <https://tools.ietf.org/html/rfc3925#section-4>`__) and
|
|
Vendor-Specific Information Option in DHCPv6 (code 17, defined in
|
|
`Section 21.17 of RFC
|
|
8415 <https://tools.ietf.org/html/rfc8415#section-21.17>`__). Vendor
|
|
class option means Vendor-Identifying Vendor Class Option in DHCPv4
|
|
(code 124; see `Section 3 of RFC
|
|
3925 <https://tools.ietf.org/html/rfc3925#section-3>`__) in DHCPv4 and
|
|
Class Option in DHCPv6 (code 16; see `Section 21.16 of RFC
|
|
8415 <https://tools.ietf.org/html/rfc8415#section-21.16>`__). Vendor
|
|
options may have sub-options that are referenced by their codes.
|
|
Vendor class options do not have sub-options, but rather data chunks,
|
|
which are referenced by index value. Index 0 means the first data
|
|
chunk, index 1 is for the second data chunk (if present), etc.
|
|
|
|
- In the vendor and vendor-class constructs an asterisk (*) or 0 can be
|
|
used to specify a wildcard enterprise-id value, i.e. it will match
|
|
any enterprise-id value.
|
|
|
|
- Vendor Class Identifier (option 60 in DHCPv4) can be accessed using the
|
|
option[60] expression.
|
|
|
|
- `RFC 3925 <https://tools.ietf.org/html/rfc3925>`__ and `RFC
|
|
8415 <https://tools.ietf.org/html/rfc8415>`__ allow for multiple
|
|
instances of vendor options to appear in a single message. The client
|
|
classification code currently examines the first instance if more
|
|
than one appear. For the vendor.enterprise and vendor-class.enterprise
|
|
expressions, the value from the first instance is returned. Please
|
|
submit a feature request on the
|
|
`Kea GitLab site <https://gitlab.isc.org/isc-projects/kea>`__ if you need
|
|
support for multiple instances.
|
|
|
|
.. table:: List of Classification Expressions
|
|
|
|
+-----------------------+-------------------------+-----------------------+
|
|
| Name | Example | Description |
|
|
+=======================+=========================+=======================+
|
|
| Equal | 'foo' == 'bar' | Compare the two |
|
|
| | | values and return |
|
|
| | | "true" or "false" |
|
|
+-----------------------+-------------------------+-----------------------+
|
|
| Not | not ('foo' == 'bar') | Logical negation |
|
|
+-----------------------+-------------------------+-----------------------+
|
|
| And | ('foo' == 'bar') and | Logical and |
|
|
| | ('bar' == 'foo') | |
|
|
+-----------------------+-------------------------+-----------------------+
|
|
| Or | ('foo' == 'bar') or | Logical or |
|
|
| | ('bar' == 'foo') | |
|
|
+-----------------------+-------------------------+-----------------------+
|
|
| Substring | substring('foobar',0,3) | Return the requested |
|
|
| | | substring |
|
|
+-----------------------+-------------------------+-----------------------+
|
|
| Concat | concat('foo','bar') | Return the |
|
|
| | | concatenation of the |
|
|
| | | strings |
|
|
+-----------------------+-------------------------+-----------------------+
|
|
| Ifelse | ifelse('foo' == | Return the branch |
|
|
| | 'bar','us','them') | value according to |
|
|
| | | the condition |
|
|
+-----------------------+-------------------------+-----------------------+
|
|
| Hexstring | hexstring('foo', '-') | Converts the value to |
|
|
| | | a hexadecimal string, |
|
|
| | | e.g. 0a:1b:2c:3e |
|
|
+-----------------------+-------------------------+-----------------------+
|
|
|
|
Logical operators
|
|
-----------------
|
|
|
|
The Not, And, and Or logical operators are the common operators. Not has
|
|
the highest precedence and Or the lowest. And and Or are (left)
|
|
associative. Parentheses around a logical expression can be used to
|
|
enforce a specific grouping; for instance, in "A and (B or C)" (without
|
|
parentheses "A and B or C" means "(A and B) or C").
|
|
|
|
Substring
|
|
---------
|
|
|
|
The substring operator "substring(value, start, length)" accepts both
|
|
positive and negative values for the starting position and the length.
|
|
For "start", a value of 0 is the first byte in the string while -1 is
|
|
the last byte. If the starting point is outside of the original string
|
|
an empty string is returned. "length" is the number of bytes to extract.
|
|
A negative number means to count towards the beginning of the string but
|
|
does not include the byte pointed to by "start". The special value "all"
|
|
means to return all bytes from start to the end of the string. If the length
|
|
is longer than the remaining portion of the string, then the entire
|
|
remaining portion is returned. Some examples may be helpful:
|
|
::
|
|
|
|
substring('foobar', 0, 6) == 'foobar'
|
|
substring('foobar', 3, 3) == 'bar'
|
|
substring('foobar', 3, all) == 'bar'
|
|
substring('foobar', 1, 4) == 'ooba'
|
|
substring('foobar', -5, 4) == 'ooba'
|
|
substring('foobar', -1, -3) == 'oba'
|
|
substring('foobar', 4, -2) == 'ob'
|
|
substring('foobar', 10, 2) == ''
|
|
|
|
|
|
Concat
|
|
------
|
|
|
|
The concat function "concat(string1, string2)" returns the concatenation
|
|
of its two arguments. For instance:
|
|
::
|
|
|
|
concat('foo', 'bar') == 'foobar'
|
|
|
|
|
|
Ifelse
|
|
------
|
|
|
|
The ifelse function "ifelse(cond, iftrue, ifelse)" returns the "iftrue"
|
|
or "ifelse" branch value following the boolean condition "cond". For
|
|
instance:
|
|
::
|
|
|
|
ifelse(option[230].exists, option[230].hex, 'none')
|
|
|
|
|
|
Hexstring
|
|
---------
|
|
|
|
The hexstring function "hexstring(binary, separator)" returns the binary
|
|
value as its hexadecimal string representation: pairs of hexadecimal
|
|
digits separated by the separator, e.g ':', '-', '' (empty separator).
|
|
::
|
|
|
|
hexstring(pkt4.mac, ':')
|
|
|
|
|
|
..
|
|
|
|
.. note::
|
|
|
|
The expression for each class is executed on each packet received. If
|
|
the expressions are overly complex, the time taken to execute them
|
|
may impact the performance of the server. Administrators who need complex or
|
|
time-consuming expressions should consider writing a
|
|
:ref:`hook <hooks-libraries>` to perform the necessary work.
|
|
|
|
.. _classification-configuring:
|
|
|
|
Configuring Classes
|
|
===================
|
|
|
|
A class contains five items: a name, a test expression, option data,
|
|
an option definition, and an only-if-required flag. The name must exist and
|
|
must be unique among all classes. The test expression, option data and
|
|
definition, and only-if-required flag are optional.
|
|
|
|
The test expression is a string containing the logical expression used
|
|
to determine membership in the class. The entire expression is in double
|
|
quotes.
|
|
|
|
The option data is a list which defines any options that should be
|
|
assigned to members of this class.
|
|
|
|
The option definition is for DHCPv4 option 43
|
|
(:ref:`dhcp4-vendor-opts` and DHCPv4 private options
|
|
(:ref:`dhcp4-private-opts`).
|
|
|
|
Usually the test expression is 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. The only-if-required flag, false by default, allows the
|
|
evaluation of the test expression only when it is required, i.e. in a
|
|
require-client-classes list of the selected subnet, shared network, or
|
|
pool.
|
|
|
|
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 data, i.e. an option data item in a
|
|
subnet takes precedence over one in a shared network, but required class in
|
|
a subnet is added after one in a shared network. The mechanism is
|
|
related to the only-if-required flag but it is not mandatory that the
|
|
flag be set to true.
|
|
|
|
In the following example, the class named "Client_foo" is defined. It is
|
|
comprised of all clients whose client ids (option 61) start with the
|
|
string "foo". Members of this class will be given 192.0.2.1 and
|
|
192.0.2.2 as their domain name servers.
|
|
|
|
::
|
|
|
|
"Dhcp4": {
|
|
"client-classes": [
|
|
{
|
|
"name": "Client_foo",
|
|
"test": "substring(option[61].hex,0,3) == 'foo'",
|
|
"option-data": [
|
|
{
|
|
"name": "domain-name-servers",
|
|
"code": 6,
|
|
"space": "dhcp4",
|
|
"csv-format": true,
|
|
"data": "192.0.2.1, 192.0.2.2"
|
|
}
|
|
]
|
|
},
|
|
...
|
|
],
|
|
...
|
|
}
|
|
|
|
This example shows a client class being defined for use by the DHCPv6
|
|
server. In it the class named "Client_enterprise" is defined. It is
|
|
comprised of all clients whose client identifiers start with the given
|
|
hex string (which would indicate a DUID based on an enterprise id of
|
|
0xAABBCCDD). Members of this class will be given an 2001:db8:0::1 and
|
|
2001:db8:2::1 as their domain name servers.
|
|
|
|
::
|
|
|
|
"Dhcp6": {
|
|
"client-classes": [
|
|
{
|
|
"name": "Client_enterprise",
|
|
"test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD'",
|
|
"option-data": [
|
|
{
|
|
"name": "dns-servers",
|
|
"code": 23,
|
|
"space": "dhcp6",
|
|
"csv-format": true,
|
|
"data": "2001:db8:0::1, 2001:db8:2::1"
|
|
}
|
|
]
|
|
},
|
|
...
|
|
],
|
|
...
|
|
}
|
|
|
|
.. _classification-using-host-reservations:
|
|
|
|
Using Static Host Reservations In Classification
|
|
================================================
|
|
|
|
Classes can be statically assigned to the clients using techniques
|
|
described in :ref:`reservation4-client-classes` and
|
|
:ref:`reservation6-client-classes`.
|
|
|
|
.. _classification-subnets:
|
|
|
|
Configuring Subnets With Class Information
|
|
==========================================
|
|
|
|
In certain cases it is beneficial to restrict access to certain subnets
|
|
only to clients that belong to a given class, using the "client-class"
|
|
keyword when defining the subnet.
|
|
|
|
Let's assume that the server is connected to a network segment that uses
|
|
the 192.0.2.0/24 prefix. The administrator of that network has decided
|
|
that addresses from the range 192.0.2.10 to 192.0.2.20 are going to be
|
|
managed by the DHCP4 server. Only clients belonging to client class
|
|
Client_foo are allowed to use this subnet. Such a configuration can be
|
|
achieved in the following way:
|
|
|
|
::
|
|
|
|
"Dhcp4": {
|
|
"client-classes": [
|
|
{
|
|
"name": "Client_foo",
|
|
"test": "substring(option[61].hex,0,3) == 'foo'",
|
|
"option-data": [
|
|
{
|
|
"name": "domain-name-servers",
|
|
"code": 6,
|
|
"space": "dhcp4",
|
|
"csv-format": true,
|
|
"data": "192.0.2.1, 192.0.2.2"
|
|
}
|
|
]
|
|
},
|
|
...
|
|
],
|
|
"subnet4": [
|
|
{
|
|
"subnet": "192.0.2.0/24",
|
|
"pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
|
|
"client-class": "Client_foo"
|
|
},
|
|
...
|
|
],,
|
|
...
|
|
}
|
|
|
|
The following example shows how to restrict access to a DHCPv6 subnet. This
|
|
configuration will restrict use of the addresses 2001:db8:1::1 to
|
|
2001:db8:1::FFFF to members of the "Client_enterprise" class.
|
|
|
|
::
|
|
|
|
"Dhcp6": {
|
|
"client-classes": [
|
|
{
|
|
"name": "Client_enterprise",
|
|
"test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD'",
|
|
"option-data": [
|
|
{
|
|
"name": "dns-servers",
|
|
"code": 23,
|
|
"space": "dhcp6",
|
|
"csv-format": true,
|
|
"data": "2001:db8:0::1, 2001:db8:2::1"
|
|
}
|
|
]
|
|
},
|
|
...
|
|
],
|
|
"subnet6": [
|
|
{
|
|
"subnet": "2001:db8:1::/64",
|
|
"pools": [ { "pool": "2001:db8:1::-2001:db8:1::ffff" } ],
|
|
"client-class": "Client_enterprise"
|
|
}
|
|
],
|
|
...
|
|
}
|
|
|
|
.. _classification-pools:
|
|
|
|
Configuring Pools With Class Information
|
|
========================================
|
|
|
|
Similar to subnets, in certain cases access to certain address or prefix
|
|
pools must be restricted to only clients that belong to a given class,
|
|
using the "client-class" when defining the pool.
|
|
|
|
Let's assume that the server is connected to a network segment that uses
|
|
the 192.0.2.0/24 prefix. The administrator of that network has decided
|
|
that addresses from the range 192.0.2.10 to 192.0.2.20 are going to be
|
|
managed by the DHCP4 server. Only clients belonging to client class
|
|
Client_foo are allowed to use this pool. Such a configuration can be
|
|
achieved in the following way:
|
|
|
|
::
|
|
|
|
"Dhcp4": {
|
|
"client-classes": [
|
|
{
|
|
"name": "Client_foo",
|
|
"test": "substring(option[61].hex,0,3) == 'foo'",
|
|
"option-data": [
|
|
{
|
|
"name": "domain-name-servers",
|
|
"code": 6,
|
|
"space": "dhcp4",
|
|
"csv-format": true,
|
|
"data": "192.0.2.1, 192.0.2.2"
|
|
}
|
|
]
|
|
},
|
|
...
|
|
],
|
|
"subnet4": [
|
|
{
|
|
"subnet": "192.0.2.0/24",
|
|
"pools": [
|
|
{
|
|
"pool": "192.0.2.10 - 192.0.2.20",
|
|
"client-class": "Client_foo"
|
|
}
|
|
]
|
|
},
|
|
...
|
|
],,
|
|
|
|
}
|
|
|
|
The following example shows how to restrict access to an address pool. This
|
|
configuration will restrict use of the addresses 2001:db8:1::1 to
|
|
2001:db8:1::FFFF to members of the "Client_enterprise" class.
|
|
|
|
::
|
|
|
|
"Dhcp6": {
|
|
"client-classes": [
|
|
{
|
|
"name": "Client_enterprise_",
|
|
"test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD'",
|
|
"option-data": [
|
|
{
|
|
"name": "dns-servers",
|
|
"code": 23,
|
|
"space": "dhcp6",
|
|
"csv-format": true,
|
|
"data": "2001:db8:0::1, 2001:db8:2::1"
|
|
}
|
|
]
|
|
},
|
|
...
|
|
],
|
|
"subnet6": [
|
|
{
|
|
"subnet": "2001:db8:1::/64",
|
|
|
|
"pools": [
|
|
{
|
|
"pool": "2001:db8:1::-2001:db8:1::ffff",
|
|
"client-class": "Client_foo"
|
|
}
|
|
]
|
|
},
|
|
...
|
|
],
|
|
...
|
|
}
|
|
|
|
Using Classes
|
|
=============
|
|
|
|
Currently classes can be used for two functions: they can supply options
|
|
to members of the class, and they can be used to choose a subnet from
|
|
which an address will be assigned to a class member.
|
|
|
|
When supplying options, options defined as part of the class definition
|
|
are considered "class globals." They will override any global options
|
|
that may be defined and in turn will be overridden by any options
|
|
defined for an individual subnet.
|
|
|
|
Classes and Hooks
|
|
=================
|
|
|
|
Hooks may be used to classify packets. This may be useful if the
|
|
expression would be complex or time-consuming to write, and could be
|
|
better or more easily written as code. Once the hook has added the proper class name
|
|
to the packet, the rest of the classification system will work as expected
|
|
in choosing a subnet and selecting options. For a description of hooks,
|
|
see :ref:`hooks-libraries`; for information on configuring classes,
|
|
see :ref:`classification-configuring` and :ref:`classification-subnets`.
|
|
|
|
Debugging Expressions
|
|
=====================
|
|
|
|
While constructing classification expressions, administrators may find
|
|
it useful to enable logging; see :ref:`logging` for a more complete
|
|
description of the logging facility.
|
|
|
|
To enable the debug statements in the classification system,
|
|
the severity must be set to "DEBUG" and the debug level to at least 55.
|
|
The specific loggers are "kea-dhcp4.eval" and "kea-dhcp6.eval".
|
|
|
|
To understand the logging statements, it is essential to understand a bit
|
|
about how expressions are evaluated; for a more complete description,
|
|
refer to the design document at
|
|
https://gitlab.isc.org/isc-projects/kea/wikis/designs/Design-documents. In
|
|
brief, there are two structures used during the evaluation of an
|
|
expression: a list of tokens which represent the expressions, and a value
|
|
stack which represents the values being manipulated.
|
|
|
|
The list of tokens is created when the configuration file is processed,
|
|
with most expressions and values being converted to a token. The list is
|
|
organized in reverse Polish notation. During execution, the list will be
|
|
traversed in order; as each token is executed it will be able to pop
|
|
values from the top of the stack and eventually push its result on the
|
|
top of the stack. Imagine the following expression:
|
|
|
|
::
|
|
|
|
"test": "substring(option[61].hex,0,3) == 'foo'",
|
|
|
|
|
|
This will result in the following tokens:
|
|
|
|
::
|
|
|
|
option, number (0), number (3), substring, text ('foo'), equals
|
|
|
|
|
|
In this example the first three tokens will simply push values onto the
|
|
stack. The substring token will then remove those three values and
|
|
compute a result that it places on the stack. The text option also
|
|
places a value on the stack and finally the equals token removes the two
|
|
tokens on the stack and places its result on the stack.
|
|
|
|
When debug logging is enabled, each time a token is evaluated it will
|
|
emit a log message indicating the values of any objects that were popped
|
|
off of the value stack and any objects that were pushed onto the value
|
|
stack.
|
|
|
|
The values will be displayed as either text, if the command is known to
|
|
use text values, or hexadecimal, if the command either uses binary values
|
|
or can manipulate either text or binary values. For expressions that pop
|
|
multiple values off the stack, the values will be displayed in the order
|
|
they were popped. For most expressions this will not matter, but for the
|
|
concat expression the values are displayed in reverse order from their
|
|
written order in the expression.
|
|
|
|
Let us assume that the following test has been entered into the
|
|
configuration. This example skips most of the configuration to
|
|
concentrate on the test.
|
|
|
|
::
|
|
|
|
"test": "substring(option[61].hex,0,3) == 'foo'",
|
|
|
|
|
|
The logging might then resemble this:
|
|
|
|
::
|
|
|
|
2016-05-19 13:35:04.163 DEBUG [kea.eval/44478] EVAL_DEBUG_OPTION Pushing option 61 with value 0x666F6F626172
|
|
2016-05-19 13:35:04.164 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string '0'
|
|
2016-05-19 13:35:04.165 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string '3'
|
|
2016-05-19 13:35:04.166 DEBUG [kea.eval/44478] EVAL_DEBUG_SUBSTRING Popping length 3, start 0, string 0x666F6F626172 pushing result 0x666F6F
|
|
2016-05-19 13:35:04.167 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string 'foo'
|
|
2016-05-19 13:35:04.168 DEBUG [kea.eval/44478] EVAL_DEBUG_EQUAL Popping 0x666F6F and 0x666F6F pushing result 'true'
|
|
|
|
|
|
..
|
|
|
|
.. note::
|
|
|
|
The debug logging may be quite verbose if there are a number of
|
|
expressions to evaluate; that is intended as an aid in helping
|
|
create and debug expressions. Administrators should plan to disable debug
|
|
logging when the expressions are working correctly. Users may also
|
|
wish to include only one set of expressions at a time in the
|
|
configuration file while debugging them, to limit the log
|
|
statements. For example, when adding a new set of expressions, an administrator
|
|
might find it more convenient to create a configuration file that
|
|
only includes the new expressions until they are working
|
|
correctly, and then add the new set to the main configuration file.
|