diff options
Diffstat (limited to 'doc/sphinx/arm/hooks-legal-log.rst')
-rw-r--r-- | doc/sphinx/arm/hooks-legal-log.rst | 1030 |
1 files changed, 1030 insertions, 0 deletions
diff --git a/doc/sphinx/arm/hooks-legal-log.rst b/doc/sphinx/arm/hooks-legal-log.rst new file mode 100644 index 0000000..961431e --- /dev/null +++ b/doc/sphinx/arm/hooks-legal-log.rst @@ -0,0 +1,1030 @@ +.. _hooks-legal-log: + +``legal_log``: Forensic Logging +=============================== + +The Forensic Logging hook library provides +hooks that record a detailed log of assignments, renewals, releases, and other +lease events into a set of log files. + +Currently this library is only available to ISC customers with a paid support +contract. + +.. note:: + + This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` + process. + +In many legal jurisdictions, companies - especially ISPs - must record +information about the addresses they have leased to DHCP clients. This +library is designed to help with that requirement. If the information +that it records is sufficient, it may be used directly. + +If a jurisdiction requires that different information be saved, users +may use the custom formatting capability to extract information from the inbound +request packet, or from the outbound response packet. Administrators are advised +to use this feature with caution, as it may affect server performance. +The custom format cannot be used for control channel commands. + +Alternatively, this library may be used as a template or an example for the +user's own custom logging hook. The logging is done as a set of hooks to allow +it to be customized to any particular need; modifying a hook library is easier +and safer than updating the core code. In addition, by using the hooks features, +users who do not need to log this information can leave it out and avoid +any performance penalties. + +Log File Naming +~~~~~~~~~~~~~~~ + +The names of the log files follow a set pattern. + +If using ``day``, ``month``, or ``year`` as the time unit, the file name follows +the format: + +:: + + path/base-name.CCYYMMDD.txt + +where ``CC`` represents the century, ``YY`` represents the year, +``MM`` represents the month, and ``DD`` represents the day. + +If using ``second`` as the time unit the file name follows the format: + +:: + + path/base-name.TXXXXXXXXXXXXXXXXXXXX.txt + +where ``XXXXXXXXXXXXXXXXXXXX`` represents the time in seconds since the beginning +of the UNIX epoch. + +When using ``second`` as the time unit, the file is rotated when +the ``count`` number of seconds pass. In contrast, when using ``day``, ``month``, +or ``year`` as the time unit, the file is rotated whenever the ``count`` of day, +month, or year starts, as applicable. + +The ``"path"`` and ``"base-name"`` are supplied in the configuration as +described below; see :ref:`forensic-log-configuration`. + +.. note:: + + When running Kea servers for both DHCPv4 and DHCPv6, the log names + must be distinct. See the examples in :ref:`forensic-log-configuration`. + +.. _forensic-log-configuration: + +Configuring the Forensic Logging Hooks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use this functionality, the hook library must be included in the +configuration of the desired DHCP server modules. The ``legal_log`` library +can save logs to a text file or to a database (created using +``kea-admin``; see :ref:`mysql-database-create` and :ref:`pgsql-database-create`). +The library is installed alongside the Kea libraries in +``[kea-install-dir]/var/lib/kea``, where ``kea-install-dir`` is determined +by the ``--prefix`` option of the configure script; it defaults to +``/usr/local``. Assuming the default value, ``kea-dhcp4`` can be configured to load +the ``legal_log`` library like this: + +.. code-block:: json + + { + "Dhcp4": { + "hooks-libraries": [ + { + "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", + "parameters": { + "path": "/var/lib/kea/log", + "base-name": "kea-forensic4" + } + } + ] + } + } + +For ``kea-dhcp6``, the configuration is: + +.. code-block:: json + + { + "Dhcp6": { + "hooks-libraries": [ + { + "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", + "parameters": { + "path": "/var/lib/kea/log", + "base-name": "kea-forensic6" + } + } + ] + } + } + +The hook library parameters for the text file configuration are: + +- ``path`` - the directory in which the forensic file(s) will be written. + The default value is ``[prefix]/var/lib/kea``. The directory must exist. + +- ``base-name`` - an arbitrary value which is used in conjunction with the + current system date to form the current forensic file name. It + defaults to ``kea-legal``. + +- ``time-unit`` - configures the time unit used to rotate the log file. Valid + values are ``second``, ``day``, ``month``, or ``year``. It defaults to + ``day``. + +- ``count`` - configures the number of time units that need to pass until the + log file is rotated. It can be any positive number, or 0, which disables log + rotation. It defaults to 1. + +If log rotation is disabled, a new file is created when the library is +loaded; the new file name is different from any previous file name. + +Additional actions can be performed just before closing the old file and after +opening the new file. These actions must point to an external executable or +script and are configured with the following settings: + +- ``prerotate`` - an external executable or script called with the name of the + file that will be closed. Kea does not wait for the process to finish. + +- ``postrotate`` - an external executable or script called with the name of the + file that was opened. Kea does not wait for the process to finish. + +Custom formatting can be enabled for logging information that can be extracted +either from the client's request packet or from the server's response packet. +Use with caution as this might affect server performance. +The custom format cannot be used for control channel commands. +Two parameters can be used towards this goal, either together or separately: + +- ``request-parser-format`` - an evaluated parsed expression used to extract and + log data from the incoming packet. + +- ``response-parser-format`` - an evaluated parsed expression used to extract and + log data from the server response packet. + +See :ref:`classification-using-expressions` for a list of expressions. +If either ``request-parser-format`` or ``response-parser-format`` is +configured, the default logging format is not used. If both of them are +configured, the resulting log message is constructed by concatenating the +data extracted from the request and the data extracted from the response. + +The custom formatting permits logging on multiple lines using the hexstring 0x0a +(ASCII code for new line). In the log file, each line is prepended +with the log timestamp. For the database backend, the data is stored +(including the newline character) in the same entry. + +Examples: + +.. code-block:: json + + { + "Dhcp6": { + "hooks-libraries": [ + { + "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", + "parameters": { + "path": "/var/lib/kea/log", + "base-name": "kea-forensic6", + "request-parser-format": "'first line' + 0x0a + 'second line'", + "response-parser-format": "'also second line' + 0x0a + 'third line'" + } + } + ] + } + } + +Some data might be available in the request or only in the response; the +data in the request packet might differ from that in the response packet. + +The lease-client context can only be printed using the default format, as this +information is not directly stored in the request packet or in the response +packet. + +The ``timestamp-format`` parameter can be used to change the timestamp logged +at the beginning of each line. Permissible formatting is the one supported by +strftime plus the '%Q' extra format which adds the microseconds subunits. The +default is: "%Y-%m-%d %H:%M:%S %Z". This parameter has no effect for the +database backends, where the timestamp is defined at the schema level. + +Examples: + +.. code-block:: json + + { + "Dhcp6": { + "hooks-libraries": [ + { + "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", + "parameters": { + "path": "/var/lib/kea/log", + "base-name": "kea-forensic6", + "timestamp-format": "%H%t%w %F%%" + } + } + ] + } + } + +Additional parameters for the database connection can be specified, e.g: + +.. code-block:: json + + { + "Dhcp6": { + "hooks-libraries": [ + { + "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", + "parameters": { + "name": "database-name", + "password": "passwd", + "type": "mysql", + "user": "user-name" + } + } + ] + } + } + +For more specific information about database-related parameters, please refer to +:ref:`database-configuration4` and :ref:`database-configuration6`. + +If it is desired to restrict forensic logging to certain subnets, the +``"legal-logging"`` boolean parameter can be specified within a user context +of these subnets. For example: + +.. code-block:: json + + { + "Dhcp4": { + "subnet4": [ + { + "subnet": "192.0.2.0/24", + "pools": [ + { + "pool": "192.0.2.1 - 192.0.2.200" + } + ], + "user-context": { + "legal-logging": false + } + } + ] + } + } + +This configuration disables legal logging for the subnet "192.0.2.0/24". If the +``"legal-logging"`` parameter is not specified, it defaults to ``true``, which +enables legal logging for the subnet. + +The following example demonstrates how to selectively disable legal +logging for an IPv6 subnet: + +.. code-block:: json + + { + "Dhcp6": { + "subnet6": [ + { + "subnet": "2001:db8:1::/64", + "pools": [ + { + "pool": "2001:db8:1::1-2001:db8:1::ffff" + } + ], + "user-context": { + "legal-logging": false + } + } + ] + } + } + +See :ref:`dhcp4-user-contexts` and :ref:`dhcp6-user-contexts` to +learn more about user contexts in Kea configuration. + +DHCPv4 Log Entries +~~~~~~~~~~~~~~~~~~ + +For DHCPv4, the library creates entries based on DHCPREQUEST, DHCPDECLINE, +and DHCPRELEASE messages, et al., and their responses. The resulting packets and +leases are taken into account, intercepted through the following hook points: + +* ``pkt4_receive`` +* ``leases4_committed`` +* ``pkt4_send`` +* ``lease4_release`` +* ``lease4_decline`` + +An entry is a single string with no embedded end-of-line markers and a +prepended timestamp, and has the following sections: + +:: + + timestamp address duration device-id {client-info} {relay-info} {user-context} + +Where: + +- ``timestamp`` - the date and time the log entry was written, in + "%Y-%m-%d %H:%M:%S %Z" strftime format ("%Z" is the time zone name). + +- ``address`` - the leased IPv4 address given out, and whether it was + assigned, renewed, or released. + +- ``duration`` - the lease lifetime expressed in days (if present), hours, + minutes, and seconds. A lease lifetime of 0xFFFFFFFF will be denoted + with the text "infinite duration." This information is not given + when the lease is released. + +- ``device-id`` - the client's hardware address shown as a numerical type and + hex-digit string. + +- ``client-info`` - the DHCP client id option (61) if present, shown as a + hex string. When its content is printable it is displayed. + +- ``relay-info`` - for relayed packets, the ``giaddr`` and the RAI ``circuit-id``, + ``remote-id``, and ``subscriber-id`` options (option 82 sub options: 1, 2 and 6), + if present. The ``circuit-id`` and ``remote-id`` are presented as hex + strings. When their content is printable it is displayed. + +- ``user-context`` - the optional user context associated with the lease. + +For instance (line breaks are added here for readability; they are not +present in the log file): + +:: + + 2018-01-06 01:02:03 CET Address: 192.2.1.100 has been renewed for 1 hrs 52 min 15 secs to a device with hardware address: + hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, + identified by circuit-id: 68:6f:77:64:79 (howdy) and remote-id: 87:f6:79:77:ef + +or for a release: + +:: + + 2018-01-06 01:02:03 CET Address: 192.2.1.100 has been released from a device with hardware address: + hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, + identified by circuit-id: 68:6f:77:64:79 (howdy) and remote-id: 87:f6:79:77:ef + +In addition to logging lease activity driven by DHCPv4 client traffic, +the hook library also logs entries for the following lease management control +channel commands: ``lease4-add``, ``lease4-update``, and ``lease4-del``. These cannot have +custom formatting. Each entry is a single string with no embedded end-of-line +markers, and it will typically have the following form: + +``lease4-add:`` + +:: + + *timestamp* Administrator added a lease of address: *address* to a device with hardware address: *device-id* + +Depending on the arguments of the add command, it may also include the +client-id and duration. + +Example: + +:: + + 2018-01-06 01:02:03 CET Administrator added a lease of address: 192.0.2.202 to a device with hardware address: + 1a:1b:1c:1d:1e:1f for 1 days 0 hrs 0 mins 0 secs + +``lease4-update:`` + +:: + + *timestamp* Administrator updated information on the lease of address: *address* to a device with hardware address: *device-id* + +Depending on the arguments of the update command, it may also include +the client-id and lease duration. + +Example: + +:: + + 2018-01-06 01:02:03 CET Administrator updated information on the lease of address: 192.0.2.202 to a device + with hardware address: 1a:1b:1c:1d:1e:1f, client-id: 1234567890 + +``lease4-del:`` deletes have two forms, one by address and one by +identifier and identifier type: + +:: + + *timestamp* Administrator deleted the lease for address: *address* + +or + +:: + + *timestamp* Administrator deleted a lease for a device identified by: *identifier-type* of *identifier* + +Currently only a type of ``@b hw-address`` (hardware address) is supported. + +Examples: + +:: + + 2018-01-06 01:02:03 CET Administrator deleted the lease for address: 192.0.2.202 + + 2018-01-06 01:02:12 CET Administrator deleted a lease for a device identified by: hw-address of 1a:1b:1c:1d:1e:1f + +The ``request-parser-format`` and ``response-parser-format`` options can be used to +extract and log data from the incoming packet and server response packet, +respectively. The configured value is an evaluated parsed expression returning a +string. A list of tokens is described in the server classification process. +Use with caution as this might affect server performance. +If either of them is configured, the default logging format is not used. +If both of them are configured, the resulting log message is constructed by +concatenating the logged data extracted from the request and the logged data +extracted from the response. + +The custom formatting permits logging on multiple lines using the hexstring 0x0a +(ASCII code for new line). In the case of the log file, each line is prepended +with the log timestamp. For the database backend, the data is stored +(including the newline character) in the same entry. + +Examples: + +.. code-block:: json + + { + "Dhcp4": { + "hooks-libraries": [ + { + "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", + "parameters": { + "name": "database-name", + "password": "passwd", + "type": "mysql", + "user": "user-name", + "request-parser-format": "'log entry' + 0x0a + 'same log entry'", + "response-parser-format": "'also same log entry' + 0x0a + 'again same log entry'" + } + } + ] + } + } + +Some data might be available in the request or in the response only, and some +data might differ in the incoming packet from the one in the response packet. + +Examples: + +.. code-block:: json + + { + "request-parser-format": "ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7, 'Address: ' + ifelse(option[50].exists, addrtotext(option[50].hex), addrtotext(pkt4.ciaddr)) + ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), '')", + "response-parser-format": "ifelse(pkt4.msgtype == 5, 'Address: ' + addrtotext(pkt4.yiaddr) + ' has been assigned for ' + uint32totext(option[51].hex) + ' seconds to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), '')" + } + +.. raw:: html + + <details><summary>Expand here!</summary> + <pre>{ + "request-parser-format": + "ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7, + 'Address: ' + + ifelse(option[50].exists, + addrtotext(option[50].hex), + addrtotext(pkt4.ciaddr)) + + ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + + ifelse(option[61].exists, + ', client-id: ' + hexstring(option[61].hex, ':'), + '') + + ifelse(pkt4.giaddr == 0.0.0.0, + '', + ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + + ifelse(option[82].option[1].exists, + ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), + '') + + ifelse(option[82].option[2].exists, + ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), + '') + + ifelse(option[82].option[6].exists, + ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), + '')), + '')", + "response-parser-format": + "ifelse(pkt4.msgtype == 5, + 'Address: ' + addrtotext(pkt4.yiaddr) + ' has been assigned for ' + uint32totext(option[51].hex) + ' seconds to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + + ifelse(option[61].exists, + ', client-id: ' + hexstring(option[61].hex, ':'), + '') + + ifelse(pkt4.giaddr == 0.0.0.0, + '', + ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + + ifelse(option[82].option[1].exists, + ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), + '') + + ifelse(option[82].option[2].exists, + ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), + '') + + ifelse(option[82].option[6].exists, + ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), + '')), + '')" + }</pre> + </details><br> + +This will log the following data on request and renew: + +:: + + Address: 192.2.1.100 has been assigned for 6735 seconds to a device with hardware address: hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, circuit-id: 68:6f:77:64:79, remote-id: 87:f6:79:77:ef, subscriber-id: 1a:2b:3c:4d:5e:6f + +This will log the following data on release and decline: + +:: + + Address: 192.2.1.100 has been released from a device with hardware address: hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, circuit-id: 68:6f:77:64:79, remote-id: 87:f6:79:77:ef, subscriber-id: 1a:2b:3c:4d:5e:6f + +A similar result can be obtained by configuring only ``request-parser-format``. + +Examples: + +.. code-block:: json + + { + "request-parser-format": "ifelse(pkt4.msgtype == 3, 'Address: ' + ifelse(option[50].exists, addrtotext(option[50].hex), addrtotext(pkt4.ciaddr)) + ' has been assigned' + ifelse(option[51].exists, ' for ' + uint32totext(option[51].hex) + ' seconds', '') + ' to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7, 'Address: ' + ifelse(option[50].exists, addrtotext(option[50].hex), addrtotext(pkt4.ciaddr)) + ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), ''))" + } + +.. raw:: html + + <details><summary>Expand here!</summary> + <pre>{ + "request-parser-format": + "ifelse(pkt4.msgtype == 3, + 'Address: ' + + ifelse(option[50].exists, + addrtotext(option[50].hex), + addrtotext(pkt4.ciaddr)) + + ' has been assigned' + + ifelse(option[51].exists, + ' for ' + uint32totext(option[51].hex) + ' seconds', + '') + + ' to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + + ifelse(option[61].exists, + ', client-id: ' + hexstring(option[61].hex, ':'), + '') + + ifelse(pkt4.giaddr == 0.0.0.0, + '', + ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + + ifelse(option[82].option[1].exists, + ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), + '') + + ifelse(option[82].option[2].exists, + ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), + '') + + ifelse(option[82].option[6].exists, + ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), + '')), + ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7, + 'Address: ' + + ifelse(option[50].exists, + addrtotext(option[50].hex), + addrtotext(pkt4.ciaddr)) + + ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + + ifelse(option[61].exists, + ', client-id: ' + hexstring(option[61].hex, ':'), + '') + + ifelse(pkt4.giaddr == 0.0.0.0, + '', + ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + + ifelse(option[82].option[1].exists, + ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), + '') + + ifelse(option[82].option[2].exists, + ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), + '') + + ifelse(option[82].option[6].exists, + ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), + '')), + ''))" + }</pre> + </details><br> + +DHCPv6 Log Entries +~~~~~~~~~~~~~~~~~~ + +For DHCPv6, the library creates entries based on REQUEST, RENEW, RELEASE, +and DECLINE messages, et al. and their responses. The resulting packets and leases +are taken into account, intercepted through the following hook points: + +* ``pkt6_receive`` +* ``leases6_committed`` +* ``pkt6_send`` +* ``lease6_release`` +* ``lease6_decline`` + +An entry is a single string with no embedded end-of-line markers and a +prepended timestamp, and has the following sections: + +:: + + timestamp address duration device-id {relay-info}* {user-context} + +Where: + +- ``timestamp`` - the date and time the log entry was written, in + "%Y-%m-%d %H:%M:%S %Z" strftime format ("%Z" is the time zone name). + +- ``address`` - the leased IPv6 address or prefix given out, and whether it + was assigned, renewed, or released. + +- ``duration`` - the lease lifetime expressed in days (if present), hours, + minutes, and seconds. A lease lifetime of 0xFFFFFFFF will be denoted + with the text "infinite duration." This information is not given + when the lease is released. + +- ``device-id`` - the client's DUID and hardware address (if present). + +- ``relay-info`` - for relayed packets the content of relay agent messages, and the + ``remote-id`` (code 37), ``subscriber-id`` (code 38), and ``interface-id`` (code 18) + options, if present. Note that the ``interface-id`` option, if present, + identifies the whole interface on which the relay agent received the message. + This typically translates to a single link in the network, but + it depends on the specific network topology. Nevertheless, this is + useful information to better pinpoint the location of the device, + so it is recorded, if present. + +- ``user-context`` - the optional user context associated with the lease. + +For instance (line breaks are added here for readability; they are not +present in the log file): + +:: + + 2018-01-06 01:02:03 PST Address:2001:db8:1:: has been assigned for 0 hrs 11 mins 53 secs + to a device with DUID: 17:34:e2:ff:09:92:54 and hardware address: hwtype=1 08:00:2b:02:3f:4e + (from Raw Socket) connected via relay at address: fe80::abcd for client on link address: 3001::1, + hop count: 1, identified by remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f and subscriber-id: 1a:2b:3c:4d:5e:6f + +or for a release: + +:: + + 2018-01-06 01:02:03 PST Address:2001:db8:1:: has been released + from a device with DUID: 17:34:e2:ff:09:92:54 and hardware address: hwtype=1 08:00:2b:02:3f:4e + (from Raw Socket) connected via relay at address: fe80::abcd for client on link address: 3001::1, + hop count: 1, identified by remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f and subscriber-id: 1a:2b:3c:4d:5e:6f + +In addition to logging lease activity driven by DHCPv6 client traffic, +the hook library also logs entries for the following lease management control channel +commands: ``lease6-add``, ``lease6-update``, and ``lease6-del``. Each entry is a +single string with no embedded end-of-line markers, and it will +typically have the following form: + +``lease6-add:`` + +:: + + *timestamp* Administrator added a lease of address: *address* to a device with DUID: *DUID* + +Depending on the arguments of the add command, it may also include the +hardware address and duration. + +Example: + +:: + + 2018-01-06 01:02:03 PST Administrator added a lease of address: 2001:db8::3 to a device with DUID: + 1a:1b:1c:1d:1e:1f:20:21:22:23:24 for 1 days 0 hrs 0 mins 0 secs + +``lease6-update:`` + +:: + + *timestamp* Administrator updated information on the lease of address: *address* to a device with DUID: *DUID* + +Depending on the arguments of the update command, it may also include +the hardware address and lease duration. + +Example: + +:: + + 2018-01-06 01:02:03 PST Administrator updated information on the lease of address: 2001:db8::3 to a device with + DUID: 1a:1b:1c:1d:1e:1f:20:21:22:23:24, hardware address: 1a:1b:1c:1d:1e:1f + +``lease6-del:`` deletes have two forms, one by address and one by +identifier and identifier type: + +:: + + *timestamp* Administrator deleted the lease for address: *address* + +or + +:: + + *timestamp* Administrator deleted a lease for a device identified by: *identifier-type* of *identifier* + +Currently only a type of ``DUID`` is supported. + +Examples: + +:: + + 2018-01-06 01:02:03 PST Administrator deleted the lease for address: 2001:db8::3 + + 2018-01-06 01:02:11 PST Administrator deleted a lease for a device identified by: duid of 1a:1b:1c:1d:1e:1f:20:21:22:23:24 + +The ``request-parser-format`` and ``response-parser-format`` options can be used to +extract and log data from the incoming packet and server response packet, +respectively. The configured value is an evaluated parsed expression returning a +string. A list of tokens is described in the server classification process. +Use with caution as this might affect server performance. +If either of them is configured, the default logging format is not used. +If both of them are configured, the resulting log message is constructed by +concatenating the logged data extracted from the request and the logged data +extracted from the response. + +The custom formatting permits logging on multiple lines using the hexstring 0x0a +(ASCII code for new line). In the case of the log file, each line is prepended +with the log timestamp. For the database backend, the data is stored +(including the newline character) in the same entry. + +Examples: + +.. code-block:: json + + { + "Dhcp6": { + "hooks-libraries": [ + { + "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", + "parameters": { + "name": "database-name", + "password": "passwd", + "type": "mysql", + "user": "user-name", + "request-parser-format": "'log entry' + 0x0a + 'same log entry'", + "response-parser-format": "'also same log entry' + 0x0a + 'again same log entry'" + } + } + ] + } + } + +Some data might be available in the request or in the response only, and some +data might differ in the incoming packet from the one in the response packet. + +Notes: + +In the case of IPv6, the packets can contain multiple IA_NA (3) or IA_PD (25) +options, each containing multiple options, including OPTION_IAADDR (5) or +OPTION_IAPREFIX (25) suboptions. +To be able to print the current lease associated with the log entry, the +forensic log hook library internally isolates the corresponding IA_NA or IA_PD +option and respective suboption matching the current lease. +The hook library will iterate over all new allocated addresses and all deleted +addresses, making each address available for logging as the current lease for +the respective logged entry. + +They are accessible using the following parser expressions: + +Current lease associated with OPTION_IAADDR: + +:: + + addrtotext(substring(option[3].option[5].hex, 0, 16)) + +Current lease associated with OPTION_IAPREFIX: + +:: + + addrtotext(substring(option[25].option[26].hex, 9, 16)) + +All other parameters of the options are available at their respective offsets +in the option. Please read RFC8415 for more details. + +Examples: + +.. code-block:: json + + { + "request-parser-format": "ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), '')", + "response-parser-format": "ifelse(pkt6.msgtype == 7, ifelse(option[3].option[5].exists and not (substring(option[3].option[5].hex, 20, 4) == 0), 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists and not (substring(option[25].option[26].hex, 4, 4) == 0), 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), '')" + } + +.. raw:: html + + <details><summary>Expand here!</summary> + <pre>{ + "request-parser-format": + "ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9, + ifelse(option[3].option[5].exists, + 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + + ifelse(relay6[0].peeraddr == '', + '', + ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + + ifelse(relay6[0].option[37].exists, + ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), + '') + + ifelse(relay6[0].option[38].exists, + ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), + '') + + ifelse(relay6[0].option[18].exists, + ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), + '')), + '') + + ifelse(option[25].option[26].exists, + 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + + ifelse(relay6[0].peeraddr == '', + '', + ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + + ifelse(relay6[0].option[37].exists, + ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), + '') + + ifelse(relay6[0].option[38].exists, + ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), + '') + + ifelse(relay6[0].option[18].exists, + ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), + '')), + ''), + '')", + "response-parser-format": + "ifelse(pkt6.msgtype == 7, + ifelse(option[3].option[5].exists and not (substring(option[3].option[5].hex, 20, 4) == 0), + 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + + ifelse(relay6[0].peeraddr == '', + '', + ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + + ifelse(relay6[0].option[37].exists, + ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), + '') + + ifelse(relay6[0].option[38].exists, + ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), + '') + + ifelse(relay6[0].option[18].exists, + ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), + '')), + '') + + ifelse(option[25].option[26].exists and not (substring(option[25].option[26].hex, 4, 4) == 0), + 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + + ifelse(relay6[0].peeraddr == '', + '', + ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + + ifelse(relay6[0].option[37].exists, + ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), + '') + + ifelse(relay6[0].option[38].exists, + ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), + '') + + ifelse(relay6[0].option[18].exists, + ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), + '')), + ''), + '')" + }</pre> + </details><br> + +This will log the following data on request, renew, and rebind for NA: + +:: + + Address: 2001:db8:1:: has been assigned for 713 seconds to a device with DUID: 17:34:e2:ff:09:92:54 connected via relay at address: fe80::abcd for client on link address: 3001::1, remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f, subscriber-id: 1a:2b:3c:4d:5e:6f, connected at location interface-id: 72:65:6c:61:79:31:3a:65:74:68:30 + +This will log the following data on request, renew and rebind for PD: + +:: + + Prefix: 2001:db8:1::/64 has been assigned for 713 seconds to a device with DUID: 17:34:e2:ff:09:92:54 connected via relay at address: fe80::abcd for client on link address: 3001::1, remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f, subscriber-id: 1a:2b:3c:4d:5e:6f, connected at location interface-id: 72:65:6c:61:79:31:3a:65:74:68:30 + +This will log the following data on release and decline for NA: + +:: + + Address: 2001:db8:1:: has been released from a device with DUID: 17:34:e2:ff:09:92:54 connected via relay at address: fe80::abcd for client on link address: 3001::1, remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f, subscriber-id: 1a:2b:3c:4d:5e:6f, connected at location interface-id: 72:65:6c:61:79:31:3a:65:74:68:30 + +This will log the following data on release and decline for PD: + +:: + + Prefix: 2001:db8:1::/64 has been released from a device with DUID: 17:34:e2:ff:09:92:54 connected via relay at address: fe80::abcd for client on link address: 3001::1, remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f, subscriber-id: 1a:2b:3c:4d:5e:6f, connected at location interface-id: 72:65:6c:61:79:31:3a:65:74:68:30 + +A similar result can be obtained by configuring only ``request-parser-format``. + +Examples: + +.. code-block:: json + + { + "request-parser-format": "ifelse(pkt6.msgtype == 3 or pkt6.msgtype == 5 or pkt6.msgtype == 6, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), ''))" + } + +.. raw:: html + + <details><summary>Expand here!</summary> + <pre>{ + "request-parser-format": + "ifelse(pkt6.msgtype == 3 or pkt6.msgtype == 5 or pkt6.msgtype == 6, + ifelse(option[3].option[5].exists, + 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + + ifelse(relay6[0].peeraddr == '', + '', + ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + + ifelse(relay6[0].option[37].exists, + ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), + '') + + ifelse(relay6[0].option[38].exists, + ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), + '') + + ifelse(relay6[0].option[18].exists, + ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), + '')), + '') + + ifelse(option[25].option[26].exists, + 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + + ifelse(relay6[0].peeraddr == '', + '', + ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + + ifelse(relay6[0].option[37].exists, + ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), + '') + + ifelse(relay6[0].option[38].exists, + ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), + '') + + ifelse(relay6[0].option[18].exists, + ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), + '')), + ''), + ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9, + ifelse(option[3].option[5].exists, + 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + + ifelse(relay6[0].peeraddr == '', + '', + ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + + ifelse(relay6[0].option[37].exists, + ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), + '') + + ifelse(relay6[0].option[38].exists, + ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), + '') + + ifelse(relay6[0].option[18].exists, + ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), + '')), + '') + + ifelse(option[25].option[26].exists, + 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + + ifelse(relay6[0].peeraddr == '', + '', + ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + + ifelse(relay6[0].option[37].exists, + ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), + '') + + ifelse(relay6[0].option[38].exists, + ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), + '') + + ifelse(relay6[0].option[18].exists, + ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), + '')), + ''), + ''))" + }</pre> + </details><br> + +.. _forensic-log-database: + +Database Backend +~~~~~~~~~~~~~~~~ + +Log entries can be inserted into a database when Kea is configured with +database backend support. Kea uses a table named ``logs``, that includes a +timestamp generated by the database software, and a text log with the same +format as files without the timestamp. + +Please refer to :ref:`mysql-database` for information on using a MySQL database; +or to :ref:`pgsql-database` for PostgreSQL database information. The ``logs`` +table is part of the Kea database schemas. + +Configuration parameters are extended by standard lease database +parameters as defined in :ref:`database-configuration4`. The ``type`` +parameter should be ``mysql``, ``postgresql`` or ``logfile``; when +it is absent or set to ``logfile``, files are used. + +This database feature is experimental. No specific tools are provided +to operate the database, but standard tools may be used, for example, +to dump the logs table from a MYSQL database: + +:: + + $ mysql --user keatest --password keatest -e "select * from logs;" + +---------------------+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+----+ + | timestamp | address | log | id | + +---------------------+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+----+ + | 2022-03-30 17:38:41 | 192.168.50.1 | Address: 192.168.50.1 has been assigned for 0 hrs 10 mins 0 secs to a device with hardware address: hwtype=1 ff:01:02:03:ff:04, client-id: 00:01:02:03:04:05:06 | 31 | + | 2022-03-30 17:38:43 | 192.168.50.1 | Address: 192.168.50.1 has been assigned for 0 hrs 10 mins 0 secs to a device with hardware address: hwtype=1 ff:01:02:03:ff:04, client-id: 00:01:02:03:04:05:06 | 32 | + | 2022-03-30 17:38:45 | 192.168.50.1 | Address: 192.168.50.1 has been assigned for 0 hrs 10 mins 0 secs to a device with hardware address: hwtype=1 ff:01:02:03:ff:04, client-id: 00:01:02:03:04:05:06 | 33 | + +---------------------+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+----+ + +Like all the other database-centric features, forensic logging supports database +connection recovery, which can be enabled by setting the ``on-fail`` parameter. +If not specified, the ``on-fail`` parameter in forensic logging defaults to +``serve-retry-continue``; +this is a change from its behavior in the Lease Commands, Host Commands, and +Configuration Backend hook libraries, where +``on-fail`` defaults to ``stop-retry-exit``. In this case, the server continues +serving clients and does not shut down even if the recovery mechanism fails. +If ``on-fail`` is set to ``serve-retry-exit``, the server will shut down if +the connection to the database backend is not restored according to the +``max-reconnect-tries`` and ``reconnect-wait-time`` parameters, but it +continues serving clients while this mechanism is activated. |