[kea-dev] Some thoughts about client classification

Stephen Morris stephen at isc.org
Mon Sep 21 19:08:45 UTC 2015


At the call today, I said that I would kick off the discussion on client
classification.  Regard this as something from which to generate ideas
rather than a done and dusted proposal.  In particular, it is
deliberately simple - the simpler it is, the earlier we are likely to
implement it.


Section 7.2.13 in the Kea manual:

http://kea.isc.org/docs/kea-guide.html#dhcp4-client-classifier

... talks about the simple classification that we already have in the
DHCPv4 and illustrates how to use it to limit access to an IPv4 subnet.

In essence, Kea looks for the vendor class identifier option and, if
present, associated the packet with a class named VENDOR_CLASS_xxx,
where "xxx" is the contents of the vendor class identifier option.

Once the class has been created, Kea can restrict the subnet from which
the address is drawn by including a

"client-class": "VENDOR_CLASS_xxx"

... statement in the "subnet" clause.

The two elements of that scheme are:

1. Creation of a client class based on some criteria.
2. Use of the class to allow/restrict the action of Kea.

Any scheme we have will need to do both of these things, and a possible
way to express this in a Kea configuration is described below.


Creation of client class
---
Client classes are defined in a clause within the "Dhcp4" or "Dhcp6"
blocks, and contain the name of the class and a single "test" clause,
that defines the criteria for inclusion in that class, e.g.

"client-classes": [
    {
        "name": "class-name",
	"test": [ selector, op, value ]
    },
    {
        "name": "class-name",
        "test": [ selector, op, value ]
    }...
]

The "test" component is an array giving the name of the selector in the
packet, a comparison operation, and a comparison value.  To keep things
simple, everything is done as string comparisons. This means that for a
first pass:

* The values of integers are converted to base 10 string format before
being checked.
* IP addresses are converted to presentation format.
* Boolean values are converted to "true" or "false".
* Binary data is converted to a hexadecimal string.

"selector" is either the name of a field in the packet or the name of an
option carried by the packet.  If an option is an array, then the
elements of the array are concatenated together (possibly separated by a
separator such as "/" or ";") before being used in the comparison. If a
substring is required, that is included in the name of the selector by
suffixing it with "[start-offset:end-offset]".

"op" is one of: "lt", "le", "eq", "ne", "ge", "gt" or "match".  The
first six are self-explanatory and compare the string derived from the
option with the value.  "match" checks if the option string matches the
regular expression.

"value" is the value to compare against.

Kea searches the "client-classes" array testing the packet against each
element of the array in order.  The first clause that matches sets the
class of the packet to the name given. If no clause matches, the packet
is unclassified (i.e. its class name is the empty string "").

Examples:

"test": ["chaddr", "eq", "08002b02deadbeef"]
... matches if the hardware address (in the "chaddr" field) is that
specified.

"test": ["chaddr[0:8]", "eq", "08002b02"]
As above but matching all clients if their hardware address starts with
the string specified.

"test": ["vendor-class-identifier", "match", "foo"]
Matches if the vendor class identifier option contains the string "foo"
somewhere in it.

"test": ["vendor-class-identifier", "match", "bar$"]
Matches if the vendor class identifier ends with the letters "bar".



Use of class information
---
The current classification scheme requires that for a configuration
block to be used, it must either contain no class name or, if it does,
the name must match the class associated with the packet.

The most frequent use of client classification would be to return a
specific address or set of options to the client depending on its class.

The Kea manual already illustrates how a specific address can be
returned (by incorporating the "client-class" keyword in a "subnet"
clause (section 7.2.13.1). As a subnet within a subnet{4,6} clause can
already contain options that override the global data, maybe the
quickets way to allow the choice of options based on class is to permit
overlapping subnets and pools within the subnet clauses.  This is
illustrated in the fuller example, below:


"Dhcp4": {
    "client-classes": [
        {
            "name": "foo",
            "test": ["chaddr[0:8]", "eq", "08002b02"]
        },
        {
# If we allow a class name to appear multiple times, we
# can implement a simple "OR" operation with no extra
# effort.
            "name": "foo",
            "test": ["chaddr[0:8]", "eq", "00de3322"]
        },
        {
            "name": "bar",
            "test": ["vendor-class-identifier", "match", "docsis"]
        },
    ],
    "subnet4": [
        {
            "subnet": "192.0.2.0/24",
            "pools": [{ "pool": "192.0.2.10 - 192.0.2.20" }],
            "client-class": "foo",
            "option-data": [
                {
                    "name": "domain-name-servers",
                    "code": 6,
                    "space": "dhcp4",
                    "csv-format": true,
                    "data": "192.0.2.3"
                },
            ],
        },
        {
# Same subnet and pool as above, but with a different value for
# the option.
            "subnet": "192.0.2.0/24",
            "pools": [{ "pool": "192.0.2.10 - 192.0.2.20" }],
            "client-class": "bar",
            "option-data": [
                {
                    "name": "domain-name-servers",
                    "code": 6,
                    "space": "dhcp4",
                    "csv-format": true,
                    "data": "10.0.0.13"
                }
            ]
        },
        {
# No client-class clause, so this will be used by all clients
# that failed to fall into one of the defined classes.
            "subnet": "192.1.2.0/24",
            "pools": [{ "pool": "192.1.2.1 - 192.1.2.254" }],
            "option-data": [
                {
                    "name": "domain-name-servers",
                    "code": 6,
                    "space": "dhcp4",
                    "csv-format": true,
                    "data": "10.0.2.1"
                }
            ]
        }
    ]
}       ...


Not elegant, true, but it seems to make maximum use of what we already have.

Thoughts?

Stephen


More information about the kea-dev mailing list