From af754e596a8dbb05ed8580c342e7fe02e08b28e0 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Sat, 13 Apr 2024 16:11:00 +0200
Subject: Adding upstream version 3.2.3+dfsg.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 .../protocols/dhcp/policy_device_options.adoc      | 310 +++++++++++++++++++++
 1 file changed, 310 insertions(+)
 create mode 100644 doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc

(limited to 'doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc')

diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc
new file mode 100644
index 0000000..05845ea
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc
@@ -0,0 +1,310 @@
+== Configure "device", "class" and "group" options
+
+Beyond the global, network and subnet options already described, most sites
+will have a number of group or class based options, and have a requirement for
+setting reply parameters against individual devices.
+
+In general, FreeRADIUS does not differentiate between "classes" (memberships
+defined by some attribute of the DHCP request) and "groups" (memberships
+defined by some manually aggregation related devices, typically based on lists
+of MAC address).
+
+The sample DHCP configuration provided with FreeRADIUS makes use of an internal
+attribute `DHCP-Group-Name` to support the setting of different options for
+different groups of devices.
+
+In general the groups to which a device belongs is determined during the
+processing of a request and these are added as instances of the
+`DHCP-Group-Name` attribute. This may be by performing a test on one or more
+request parameters (akin to a "class"), hash-based lookup of up all of part of
+an attribute in a local list (akin to a "subclass"), or doing the same using a
+remote datastore (SQL, LDAP, REST API, etc).
+
+FreeRADIUS can then iterate over `DHCP-Group-Name` to set group-specific
+options.
+
+We describe some of these options in more detail.
+
+=== Directly in Policy
+
+Simple class options can be written directly into policy.  This is most
+suited to those options that rarely change and are based on attributes in the
+request such as the `User-Class`.
+
+Consider the ISC DHCP configuration snippet:
+
+[source,iscdhcp]
+----
+filename "undionly.kpxe";
+class "pxeclient" {
+    match option substring(user-class,0,4);
+}
+subclass "pxeclient" "iPXE" {
+    filename "http://my.web.server/boot_script.php";
+}
+----
+
+Or the equivalent Kea configuration:
+
+[source,isckea]
+----
+"Dhcp4": {
+    "option-data": [
+        { "name": "boot-file-name", "data": "undionly.kpxe" }
+    ],
+    "client-classes": [
+        {
+            "name": "pxeclient",
+            "test": "substring(option[77],0,4) == 'iPXE'",
+            "option-data": [
+               {
+                   "name": "boot-file-name",
+                   "data": "http://my.web.server/boot_script.php"
+               }
+            ]
+        }
+    ]
+    ...
+}
+----
+
+These define the "filename" DHCP option differently based on whether or not the
+supplied "user-class" option begins with "iPXE".
+
+FreeRADIUS provides multiple ways for this to be configured.
+
+For example, the following "unlang" policy implements the class options defined
+above:
+
+[source,unlang]
+----
+if (&DHCP-User-Class && "%{substring:&DHCP-User-Class 0 4}" == "iPXE") {
+    update reply {
+        &DHCP-Boot-Filename := "http://my.web.server/boot_script.php"
+    }
+} else {
+    update reply {
+        &DHCP-Boot-Filename := "undionly.kpxe"
+    }
+}
+----
+
+Policy-based configuration of DHCP options is also useful for complex matching.
+For example, the following Unlang sets the DHCP-Boot-Filename parameter based
+on the request's DHCP-Client-Identifier using regular expression captures,
+provided that it matches the given format:
+
+[source,unlang]
+----
+if (&DHCP-Client-Identifier && \
+        "%{string:DHCP-Client-Identifier}" =~ /^RAS([0-9])-site([A-Z])$/) {
+    update reply {
+        &DHCP-Boot-Filename := "rasboot-%{1}-%{2}.kpxe"
+    }
+}
+----
+
+=== In Text Files
+
+The `files` module that has already been described for global, network and
+subnet options can also be used to apply options to groups of clients.
+
+Firstly we must defined a mapping from a set of clients clients to their
+respective groups.  One option for this is to use the `passwd` module, for
+which a sample configuration is included.
+
+Firstly symlink or copy the module configuration
+`<raddb>/mods-available/dhcp_passwd` into `<raddb>/mods-enabled/`.  The
+suggested configuration expects the group membership file to be in
+`<raddb>/mods-config/files/dhcp_groups` and take the form of:
+
+[source,config]
+----
+<group1 name>|<hardware address>,<hardware address>,<hardware address>
+<group2 name>|<hardware address>,<hardware address>
+----
+
+i.e. one line for each group starting with the group name followed by a pipe
+character and then a comma-separated list of hardware addresses.
+
+The `allow_multiple_keys` option allows for a host to be a member of
+more than one group.
+
+Sample configuration for looking up group options is contained in
+`<raddb>/policy.d/dhcp` in the `dhcp_group_options` policy and in
+`<raddb>/mods-available/dhcp_files` as the `dhcp_set_group_options` instance.
+
+The same data file `<raddb>/mods-config/files/dhcp` is used to lookup
+group options as was used for global and network options.  In this instance,
+add entries with the group name as the key such as:
+
+[source,config]
+----
+group1
+       DHCP-Log-Server := 10.10.0.100,
+       DHCP-LPR-Server := 10.10.0.200
+
+group2
+       DHCP-LPR-Server := 192.168.20.200
+----
+
+=== In the SQL Database
+
+Policy and files are both read during startup and editing them while
+FreeRADIUS is running will not result in any changes in behaviour.  If
+you require regular changes to DHCP options, then storing them in
+an SQL database provides greater flexibility since the queries will be run in
+response to each DHCP packet rather than requiring the server to be restarted.
+
+DHCP reply options for devices (including network-specific options) can be
+fetched from SQL using an arbitrary lookup key. This can be performed multiple
+times as necessary using different contexts, for example to first set
+subnet-specific options and then to set group-specific options.
+
+The default schema contains three tables to support this:
+
+"dhcpreply" contains reply options for a given identifier (e.g. MAC Address):
+
+.dhcpreply table
+|===
+|Identifier |Attribute |Op |Value |Context
+
+|`02:01:aa:bb:cc:dd` |`DHCP-Log-Server` |`:=` |`192.0.2.10` |`by-mac`
+|`02:01:aa:bb:cc:dd` |`DHCP-LPR-Server` |`:=` |`192.0.2.11` |`by-mac`
+|`02:01:aa:bb:cc:dd` |`Fall-Through`    |`:=` |`Yes`        |`by-mac`
+|===
+
+"dhcpgroup" maps identifiers to a group of options that can be shared:
+
+.dhcpgroup table
+|===
+|Identifier |GroupName |Priority |Context
+
+|`02:01:aa:bb:cc:dd` |`salesdept` |`10` |`by-mac`
+|===
+
+"dhcpgroupreply" contains reply options for each group:
+
+.dhcpgroupreply table
+|===
+|GroupName |Attribute |Op |Value |Context
+
+|`salesdept` |`DHCP-NTP-Servers` |`:=` |`192.0.2.20` |`by-mac`
+|`salesdept` |`DHCP-Log-Server`  |`+=` |`192.0.2.21` |`by-mac`
+|`salesdept` |`DHCP-LPR-Server`  |`^=` |`192.0.2.22` |`by-mac`
+|===
+
+Within the context of assigning options directly to devices, as well as to
+manually-curated groups of devices keyed by their MAC address:
+
+  - Place device-specific options in the "dhcpreply" table.
+  - Add `Fall-Through := Yes` to the options in the "dhcpreply" table in order
+    to trigger group lookups, which are disabled by default.
+  - Place entries in the "dhcpgroup" `identifier = <MAC-Address>, groupname = <group>, priority =
+    <priority>` in the "dhcpgroup" table to map a device to its groups by
+    priority.
+  - Place the grouped options in the "dhcpgroupreply" table.
+  - For each of the above, set `Context` to something by which the option
+    lookup is referred to in the policy, for example `Context = 'by-mac'`.
+
+For the above example you would add the following to the DHCP virtual server to
+perform reply option lookup using the device's MAC address against the `by-mac`
+context:
+
+[source,unlang]
+----
+update control {
+        &DHCP-SQL-Option-Context := "by-mac"
+        &DHCP-SQL-Option-Identifier := &request:DHCP-Client-Hardware-Address
+}
+dhcp_sql.authorize
+----
+
+In the above, the DHCP reply options would be assigned to a device with MAC
+address 02:01:aa:bb:cc:dd as follows:
+
+  - Firstly, the `DHCP-Log-Server` option would be set to `192.0.2.10` and the
+    `DHCP-LPR-Server` option set to `192.0.2.11`.
+  - `Fall-Through` is set, so the group mapping is then queried which
+    determines that the device belongs to a single `salesdept` group.
+  - Finally, the options for the `salesdept` group are now merged, setting a
+    `DHCP-NTP-Servers` option to `192.0.2.20`, appending an additional
+    `DHCP-Log-Server` option set to `192.0.2.21`, and prepending an additional
+    `DHCP-LPR-Server` option set to `192.0.2.22`.
+
+If instead you wanted to perform a "subclass" lookup based on the first three
+octets of the device's MAC address then with tables containing the following
+sample data you could invoke an SQL lookup as shown:
+
+."dhcpreply" table:
+|===
+|Identifier |Attribute |Op |Value |Context
+
+|`000393` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
+|`000a27` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
+|`f40304` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
+|===
+
+."dhcpgroup" table:
+|===
+|Identifier |GroupName |Priority |Context
+
+|`000393` |`apple`  |`10` |`class-vendor`
+|`000a27` |`apple`  |`10` |`class-vendor`
+|`f40304` |`google` |`10` |`class-vendor`
+|===
+
+."dhcpgroupreply" table:
+|===
+|GroupName |Attribute |Op |Value |Context
+
+|`apple`  |`DHCP-Boot-Filename` |`:=` |`apple.efi`  |`class-vendor`
+|`google` |`DHCP-Boot-Filename` |`:=` |`google.efi` |`class-vendor`
+|===
+
+
+[source,unlang]
+----
+update control {
+        &DHCP-SQL-Option-Context := "class-vendor"
+        &DHCP-SQL-Option-Identifier := \
+            "%{substring:%{hex:&DHCP-Client-Hardware-Address} 0 6}"
+}
+dhcp_sql.authorize
+----
+
+The file `policy.d/dhcp` contains a policy named `dhcp_policy_sql` which
+provides further worked examples for different types of option lookups.
+
+=== Testing "device", "class" and "group" options
+
+You should now test that any device-related options that you have configured
+using the various methods available are applied successfully by generating
+packets containing those parameters based upon which the reply options are set.
+
+For example, to test the iPXE user class example above you might want to
+generate a request as follows:
+
+[source,shell]
+----
+cat <<EOF > dhcp-packet-ipxe-boot.txt
+DHCP-Message-Type := DHCP-Discover
+DHCP-Client-Hardware-Address := 02:01:aa:bb:cc:dd
+DHCP-User-Class := "iPXE-class-abc"
+EOF
+----
+
+To which you would expect to see a response such as:
+
+.Example output from dhcpclient
+===============================
+ dhcpclient: ...
+ ----------------------------------------------------------------------
+ Waiting for DHCP replies for: 5.000000
+ ----------------------------------------------------------------------
+ ...
+ DHCP-Message-Type = DHCP-Offer
+ DHCP-Your-IP-Address = 1.2.3.4
+ DHCP-Boot-Filename := "http://my.web.server/boot_script.php"
+ ...
+===============================
-- 
cgit v1.2.3

